This is an automated email from the ASF dual-hosted git repository.

fanningpj pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/poi.git


The following commit(s) were added to refs/heads/trunk by this push:
     new c953a8ff22 Add XSSFOptimiser - XSSF equivalent of HSSFOptimiser (#952)
c953a8ff22 is described below

commit c953a8ff229a41b9313aee8ae4e08cf619e96284
Author: Gili Tzabari <[email protected]>
AuthorDate: Mon Dec 1 13:30:27 2025 -0500

    Add XSSFOptimiser - XSSF equivalent of HSSFOptimiser (#952)
    
    * Add XSSFOptimiser - XSSF equivalent of HSSFOptimiser
    
    Adds optimization methods for XSSF workbooks to address style explosion
    issues. HSSFOptimiser exists for HSSF but no equivalent existed for XSSF.
    
    Methods added:
    - optimiseCellStyles(XSSFWorkbook): Remaps cells using duplicate styles
      to canonical (first) occurrence
    - optimiseFonts(XSSFWorkbook): Updates style font references to canonical
      fonts
    
    Implementation uses a safe remapping approach rather than removing entries
    from XML, which avoids XmlValueDisconnectedException issues caused by
    StylesTable's internal list becoming disconnected from CTStylesheet.
    
    Includes comprehensive test coverage in TestXSSFOptimiser.
    
    Closes #951
    
    * Address PR review comments
    
    - Add "since 6.0.0" javadoc tag to XSSFOptimiser class
    - Use Locale.ROOT in String.format() to comply with forbidden-apis check
---
 .../apache/poi/xssf/usermodel/XSSFOptimiser.java   | 249 +++++++++++++++++
 .../poi/xssf/usermodel/TestXSSFOptimiser.java      | 305 +++++++++++++++++++++
 2 files changed, 554 insertions(+)

diff --git 
a/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFOptimiser.java 
b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFOptimiser.java
new file mode 100755
index 0000000000..9d2ad208b6
--- /dev/null
+++ b/poi-ooxml/src/main/java/org/apache/poi/xssf/usermodel/XSSFOptimiser.java
@@ -0,0 +1,249 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You 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.xssf.usermodel;
+
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.Color;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.xssf.model.StylesTable;
+import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCellXfs;
+import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTStylesheet;
+import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTXf;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * XSSF equivalent of HSSFOptimiser.
+ *
+ * <p>Provides methods to optimise cell styles and fonts in XSSF workbooks
+ * by remapping cells that use duplicate styles/fonts to use canonical
+ * (first) occurrences.</p>
+ *
+ * <p>This helps avoid "style explosion" issues where Excel workbooks 
accumulate
+ * too many style definitions, causing performance problems or file 
corruption.</p>
+ *
+ * <p>Usage:</p>
+ * <pre>
+ *   XSSFWorkbook workbook = new XSSFWorkbook(file);
+ *   XSSFOptimiser.optimiseFonts(workbook);
+ *   XSSFOptimiser.optimiseCellStyles(workbook);
+ *   workbook.write(outputStream);
+ * </pre>
+ *
+ * <p>Note: Unlike HSSFOptimiser which can remove unused styles from the binary
+ * format, this implementation remaps cells to canonical styles without 
removing
+ * entries from the XML. This is because XSSF's StylesTable maintains a 
separate
+ * Java list from the CTStylesheet XML, and removing entries can cause
+ * XmlValueDisconnectedException on save. The remapping approach prevents
+ * further style explosion and is safe.</p>
+ *
+ * @see org.apache.poi.hssf.usermodel.HSSFOptimiser
+ * @since 6.0.0
+ */
+public final class XSSFOptimiser {
+
+    private XSSFOptimiser() {
+        // no instances of this class
+    }
+
+    /**
+     * Goes through the Workbook, optimising the cell styles by remapping
+     * cells that use duplicate styles to use the canonical (first) occurrence.
+     *
+     * <p>Two styles are considered duplicates if they have identical 
properties:
+     * font properties (not just font index), data format, alignment, borders,
+     * fill colors, etc.</p>
+     *
+     * <p>For best results, call {@link #optimiseFonts(XSSFWorkbook)} 
first.</p>
+     *
+     * @param workbook The workbook to optimise
+     * @return The number of cells that were remapped to canonical styles
+     */
+    public static int optimiseCellStyles(XSSFWorkbook workbook) {
+        int numStyles = workbook.getNumCellStyles();
+
+        if (numStyles <= 1) {
+            return 0; // Nothing to optimize
+        }
+
+        // Step 1: Build signature map for duplicate detection
+        Map<String, Integer> signatureToCanonical = new LinkedHashMap<>();
+        Map<Integer, Integer> oldToCanonical = new HashMap<>();
+
+        for (int i = 0; i < numStyles; i++) {
+            XSSFCellStyle style = workbook.getCellStyleAt(i);
+            String signature = getStyleSignature(style);
+
+            if (signatureToCanonical.containsKey(signature)) {
+                // Duplicate found - map to earlier canonical style
+                oldToCanonical.put(i, signatureToCanonical.get(signature));
+            } else {
+                // First occurrence - this is the canonical style
+                signatureToCanonical.put(signature, i);
+                oldToCanonical.put(i, i);
+            }
+        }
+
+        // Step 2: Remap all cells to use canonical styles
+        int cellsRemapped = 0;
+        for (int sheetIdx = 0; sheetIdx < workbook.getNumberOfSheets(); 
sheetIdx++) {
+            XSSFSheet sheet = workbook.getSheetAt(sheetIdx);
+            for (Row row : sheet) {
+                if (row == null) continue;
+                for (Cell cell : row) {
+                    if (cell == null) continue;
+                    int currentIdx = cell.getCellStyle().getIndex();
+                    int canonicalIdx = oldToCanonical.getOrDefault(currentIdx, 
currentIdx);
+
+                    if (canonicalIdx != currentIdx) {
+                        
cell.setCellStyle(workbook.getCellStyleAt(canonicalIdx));
+                        cellsRemapped++;
+                    }
+                }
+            }
+        }
+
+        return cellsRemapped;
+    }
+
+    /**
+     * Goes through the Workbook, optimising the fonts by updating cell styles
+     * that reference duplicate fonts to use the canonical (first) occurrence.
+     *
+     * <p>Two fonts are considered duplicates if they have identical 
properties:
+     * name, size, bold, italic, strikeout, underline, color, typeOffset, 
charset.</p>
+     *
+     * @param workbook The workbook to optimise
+     * @return The number of duplicate fonts found
+     */
+    public static int optimiseFonts(XSSFWorkbook workbook) {
+        int numFonts = workbook.getNumberOfFonts();
+
+        if (numFonts <= 1) {
+            return 0; // Nothing to optimize
+        }
+
+        // Step 1: Build signature map for duplicate detection
+        Map<String, Integer> signatureToCanonical = new LinkedHashMap<>();
+        Map<Integer, Integer> oldToCanonical = new HashMap<>();
+        int duplicatesFound = 0;
+
+        for (int i = 0; i < numFonts; i++) {
+            XSSFFont font = workbook.getFontAt(i);
+            String signature = getFontSignature(font);
+
+            if (signatureToCanonical.containsKey(signature)) {
+                oldToCanonical.put(i, signatureToCanonical.get(signature));
+                duplicatesFound++;
+            } else {
+                signatureToCanonical.put(signature, i);
+                oldToCanonical.put(i, i);
+            }
+        }
+
+        if (duplicatesFound == 0) {
+            return 0; // No duplicates to remap
+        }
+
+        // Step 2: Update font references in cell styles' CTXf entries
+        StylesTable stylesTable = workbook.getStylesSource();
+        CTStylesheet ctStylesheet = stylesTable.getCTStylesheet();
+        CTCellXfs ctXfs = ctStylesheet.getCellXfs();
+
+        if (ctXfs != null) {
+            for (int i = 0; i < ctXfs.sizeOfXfArray(); i++) {
+                CTXf xf = ctXfs.getXfArray(i);
+                if (xf.isSetFontId()) {
+                    int oldFontId = (int) xf.getFontId();
+                    int canonicalFontId = 
oldToCanonical.getOrDefault(oldFontId, oldFontId);
+                    if (canonicalFontId != oldFontId) {
+                        xf.setFontId(canonicalFontId);
+                    }
+                }
+            }
+        }
+
+        return duplicatesFound;
+    }
+
+    /**
+     * Generate a signature string for a cell style to detect duplicates.
+     * Uses font PROPERTIES (not index) so styles with identical but separate 
fonts
+     * are detected as duplicates.
+     */
+    private static String getStyleSignature(XSSFCellStyle style) {
+        StringBuilder sb = new StringBuilder();
+        // Use font properties, not index, so identical fonts are detected
+        XSSFFont font = style.getFont();
+        sb.append("font:").append(getFontSignature(font)).append(";");
+        sb.append("fmt:").append(style.getDataFormat()).append(";");
+        sb.append("align:").append(style.getAlignment()).append(";");
+        sb.append("valign:").append(style.getVerticalAlignment()).append(";");
+        sb.append("wrap:").append(style.getWrapText()).append(";");
+        sb.append("rotation:").append(style.getRotation()).append(";");
+        sb.append("indent:").append(style.getIndention()).append(";");
+        sb.append("borderL:").append(style.getBorderLeft()).append(";");
+        sb.append("borderR:").append(style.getBorderRight()).append(";");
+        sb.append("borderT:").append(style.getBorderTop()).append(";");
+        sb.append("borderB:").append(style.getBorderBottom()).append(";");
+        
sb.append("fillFg:").append(colorToString(style.getFillForegroundColorColor())).append(";");
+        
sb.append("fillBg:").append(colorToString(style.getFillBackgroundColorColor())).append(";");
+        sb.append("fillPat:").append(style.getFillPattern()).append(";");
+        sb.append("locked:").append(style.getLocked()).append(";");
+        sb.append("hidden:").append(style.getHidden()).append(";");
+        return sb.toString();
+    }
+
+    /**
+     * Generate a signature string for a font to detect duplicates.
+     */
+    private static String getFontSignature(XSSFFont font) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("name:").append(font.getFontName()).append(";");
+        sb.append("size:").append(font.getFontHeightInPoints()).append(";");
+        sb.append("bold:").append(font.getBold()).append(";");
+        sb.append("italic:").append(font.getItalic()).append(";");
+        sb.append("strike:").append(font.getStrikeout()).append(";");
+        sb.append("underline:").append(font.getUnderline()).append(";");
+        
sb.append("color:").append(colorToString(font.getXSSFColor())).append(";");
+        sb.append("typeOffset:").append(font.getTypeOffset()).append(";");
+        sb.append("charset:").append(font.getCharSet()).append(";");
+        return sb.toString();
+    }
+
+    /**
+     * Convert a color to a string for comparison.
+     */
+    private static String colorToString(Color color) {
+        if (color == null) {
+            return "null";
+        }
+        if (color instanceof XSSFColor) {
+            XSSFColor xssfColor = (XSSFColor) color;
+            byte[] rgb = xssfColor.getRGB();
+            if (rgb != null) {
+                return String.format(Locale.ROOT, "#%02X%02X%02X", rgb[0] & 
0xFF, rgb[1] & 0xFF, rgb[2] & 0xFF);
+            }
+            return "indexed:" + xssfColor.getIndexed();
+        }
+        return color.toString();
+    }
+}
diff --git 
a/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFOptimiser.java 
b/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFOptimiser.java
new file mode 100755
index 0000000000..8587cffbe3
--- /dev/null
+++ 
b/poi-ooxml/src/test/java/org/apache/poi/xssf/usermodel/TestXSSFOptimiser.java
@@ -0,0 +1,305 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You 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.xssf.usermodel;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.ss.usermodel.DataFormat;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.Row;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for {@link XSSFOptimiser}.
+ */
+class TestXSSFOptimiser {
+
+    /**
+     * Test that optimiseCellStyles remaps cells with duplicate styles to 
canonical style.
+     */
+    @Test
+    void testOptimiseCellStylesRemapsDuplicates() throws Exception {
+        try (XSSFWorkbook workbook = new XSSFWorkbook()) {
+            XSSFSheet sheet = workbook.createSheet("Test");
+
+            // Create 10 cells, each with a "new" style that is actually 
identical
+            for (int i = 0; i < 10; i++) {
+                Row row = sheet.createRow(i);
+                Cell cell = row.createCell(0);
+                cell.setCellValue("Value " + i);
+
+                // Create a "new" style for each cell (causes style explosion)
+                CellStyle style = workbook.createCellStyle();
+                Font font = workbook.createFont();
+                font.setFontName("Calibri");
+                font.setFontHeightInPoints((short) 11);
+                font.setBold(false);
+                style.setFont(font);
+                cell.setCellStyle(style);
+            }
+
+            int stylesBeforeOptimize = workbook.getNumCellStyles();
+            assertTrue(stylesBeforeOptimize > 5,
+                "Should have created multiple styles before optimization, got: 
" + stylesBeforeOptimize);
+
+            // Optimize - returns number of cells remapped
+            int cellsRemapped = XSSFOptimiser.optimiseCellStyles(workbook);
+            assertTrue(cellsRemapped > 0,
+                "Should have remapped cells to canonical styles, got: " + 
cellsRemapped);
+
+            // Verify all cells now reference the same canonical style index
+            int canonicalStyleIdx = 
sheet.getRow(0).getCell(0).getCellStyle().getIndex();
+            for (int i = 1; i < 10; i++) {
+                Cell cell = sheet.getRow(i).getCell(0);
+                assertEquals(canonicalStyleIdx, cell.getCellStyle().getIndex(),
+                    "Cell " + i + " should use canonical style index");
+            }
+
+            // Verify cells still have correct values
+            for (int i = 0; i < 10; i++) {
+                Cell cell = sheet.getRow(i).getCell(0);
+                assertEquals("Value " + i, cell.getStringCellValue());
+            }
+        }
+    }
+
+    /**
+     * Test that optimiseCellStyles handles unused styles gracefully.
+     */
+    @Test
+    void testOptimiseCellStylesHandlesUnusedStyles() throws Exception {
+        try (XSSFWorkbook workbook = new XSSFWorkbook()) {
+            XSSFSheet sheet = workbook.createSheet("Test");
+
+            // Create a cell with one style
+            Row row = sheet.createRow(0);
+            Cell cell = row.createCell(0);
+            cell.setCellValue("Used style");
+            CellStyle usedStyle = workbook.createCellStyle();
+            usedStyle.setAlignment(HorizontalAlignment.CENTER);
+            cell.setCellStyle(usedStyle);
+
+            // Create several unused styles
+            for (int i = 0; i < 5; i++) {
+                CellStyle unusedStyle = workbook.createCellStyle();
+                unusedStyle.setAlignment(HorizontalAlignment.LEFT);
+                // Never apply this style to any cell
+            }
+
+            int stylesBeforeOptimize = workbook.getNumCellStyles();
+            assertTrue(stylesBeforeOptimize >= 6,
+                "Should have at least 6 styles (1 default + 1 used + 5 
unused), got: " + stylesBeforeOptimize);
+
+            // Optimize - should not throw, returns 0 (no duplicates to remap)
+            int cellsRemapped = XSSFOptimiser.optimiseCellStyles(workbook);
+            assertEquals(0, cellsRemapped, "No duplicate styles to remap");
+
+            // Verify the used cell still has correct alignment
+            CellStyle cellStyle = cell.getCellStyle();
+            assertEquals(HorizontalAlignment.CENTER, cellStyle.getAlignment(),
+                "Cell should still have CENTER alignment after optimization");
+        }
+    }
+
+    /**
+     * Test that optimiseCellStyles preserves different styles.
+     */
+    @Test
+    void testOptimiseCellStylesPreservesDifferentStyles() throws Exception {
+        try (XSSFWorkbook workbook = new XSSFWorkbook()) {
+            XSSFSheet sheet = workbook.createSheet("Test");
+
+            // Cell with bold style
+            Row row1 = sheet.createRow(0);
+            Cell cell1 = row1.createCell(0);
+            cell1.setCellValue("Bold");
+            CellStyle boldStyle = workbook.createCellStyle();
+            Font boldFont = workbook.createFont();
+            boldFont.setBold(true);
+            boldStyle.setFont(boldFont);
+            cell1.setCellStyle(boldStyle);
+
+            // Cell with italic style
+            Row row2 = sheet.createRow(1);
+            Cell cell2 = row2.createCell(0);
+            cell2.setCellValue("Italic");
+            CellStyle italicStyle = workbook.createCellStyle();
+            Font italicFont = workbook.createFont();
+            italicFont.setItalic(true);
+            italicStyle.setFont(italicFont);
+            cell2.setCellStyle(italicStyle);
+
+            // Cell with number format
+            Row row3 = sheet.createRow(2);
+            Cell cell3 = row3.createCell(0);
+            cell3.setCellValue(1234.56);
+            CellStyle numStyle = workbook.createCellStyle();
+            DataFormat format = workbook.createDataFormat();
+            numStyle.setDataFormat(format.getFormat("#,##0.00"));
+            cell3.setCellStyle(numStyle);
+
+            // Optimize
+            XSSFOptimiser.optimiseCellStyles(workbook);
+
+            // Verify each cell retains its distinct formatting
+            Font font1 = 
workbook.getFontAt(cell1.getCellStyle().getFontIndex());
+            assertTrue(font1.getBold(), "Cell 1 should still be bold");
+
+            Font font2 = 
workbook.getFontAt(cell2.getCellStyle().getFontIndex());
+            assertTrue(font2.getItalic(), "Cell 2 should still be italic");
+
+            String numFormat = cell3.getCellStyle().getDataFormatString();
+            assertTrue(numFormat.contains("#,##0"),
+                "Cell 3 should still have number format, got: " + numFormat);
+        }
+    }
+
+    /**
+     * Test that optimiseFonts remaps style font references to canonical fonts.
+     */
+    @Test
+    void testOptimiseFontsRemapsDuplicates() throws Exception {
+        try (XSSFWorkbook workbook = new XSSFWorkbook()) {
+            XSSFSheet sheet = workbook.createSheet("Test");
+
+            // Create cells with duplicate fonts
+            for (int i = 0; i < 10; i++) {
+                Row row = sheet.createRow(i);
+                Cell cell = row.createCell(0);
+                cell.setCellValue("Value " + i);
+
+                // Create a "new" font for each cell (identical fonts)
+                CellStyle style = workbook.createCellStyle();
+                Font font = workbook.createFont();
+                font.setFontName("Arial");
+                font.setFontHeightInPoints((short) 12);
+                font.setBold(true);
+                style.setFont(font);
+                cell.setCellStyle(style);
+            }
+
+            int fontsBeforeOptimize = workbook.getNumberOfFonts();
+            assertTrue(fontsBeforeOptimize > 5,
+                "Should have created multiple fonts before optimization, got: 
" + fontsBeforeOptimize);
+
+            // Optimize fonts - returns number of duplicates found
+            int duplicatesFound = XSSFOptimiser.optimiseFonts(workbook);
+            assertTrue(duplicatesFound > 0,
+                "Should have found duplicate fonts, got: " + duplicatesFound);
+
+            // Verify cells still have bold Arial font (formatting preserved)
+            for (int i = 0; i < 10; i++) {
+                Cell cell = sheet.getRow(i).getCell(0);
+                Font font = 
workbook.getFontAt(cell.getCellStyle().getFontIndex());
+                assertEquals("Arial", font.getFontName(), "Font name should be 
preserved");
+                assertTrue(font.getBold(), "Font should still be bold");
+            }
+        }
+    }
+
+    /**
+     * Test that workbook is saveable after optimization.
+     */
+    @Test
+    void testWorkbookSaveableAfterOptimization() throws Exception {
+        try (XSSFWorkbook workbook = new XSSFWorkbook()) {
+            XSSFSheet sheet = workbook.createSheet("Test");
+
+            // Create style explosion
+            for (int i = 0; i < 20; i++) {
+                Row row = sheet.createRow(i);
+                Cell cell = row.createCell(0);
+                cell.setCellValue("Value " + i);
+
+                CellStyle style = workbook.createCellStyle();
+                Font font = workbook.createFont();
+                font.setFontName("Calibri");
+                style.setFont(font);
+                cell.setCellStyle(style);
+            }
+
+            // Optimize
+            XSSFOptimiser.optimiseCellStyles(workbook);
+            XSSFOptimiser.optimiseFonts(workbook);
+
+            // Save to byte array (should not throw 
XmlValueDisconnectedException)
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            workbook.write(baos);
+            byte[] savedBytes = baos.toByteArray();
+
+            assertTrue(savedBytes.length > 0, "Saved workbook should have 
content");
+
+            // Verify we can read it back
+            try (XSSFWorkbook reloaded = new XSSFWorkbook(new 
ByteArrayInputStream(savedBytes))) {
+                XSSFSheet reloadedSheet = reloaded.getSheetAt(0);
+                assertEquals("Value 0", 
reloadedSheet.getRow(0).getCell(0).getStringCellValue());
+                assertEquals("Value 19", 
reloadedSheet.getRow(19).getCell(0).getStringCellValue());
+            }
+        }
+    }
+
+    /**
+     * Test that optimization preserves formulas.
+     */
+    @Test
+    void testOptimizationPreservesFormulas() throws Exception {
+        try (XSSFWorkbook workbook = new XSSFWorkbook()) {
+            XSSFSheet sheet = workbook.createSheet("Test");
+
+            // Create cells with values
+            for (int i = 0; i < 5; i++) {
+                Row row = sheet.createRow(i);
+                Cell cell = row.createCell(0);
+                cell.setCellValue(i + 1);
+
+                CellStyle style = workbook.createCellStyle();
+                style.setAlignment(HorizontalAlignment.RIGHT);
+                cell.setCellStyle(style);
+            }
+
+            // Create formula cell
+            Row sumRow = sheet.createRow(5);
+            Cell sumCell = sumRow.createCell(0);
+            sumCell.setCellFormula("SUM(A1:A5)");
+            CellStyle sumStyle = workbook.createCellStyle();
+            Font boldFont = workbook.createFont();
+            boldFont.setBold(true);
+            sumStyle.setFont(boldFont);
+            sumCell.setCellStyle(sumStyle);
+
+            // Optimize
+            XSSFOptimiser.optimiseCellStyles(workbook);
+
+            // Verify formula is preserved
+            Cell formulaCell = sheet.getRow(5).getCell(0);
+            assertEquals(CellType.FORMULA, formulaCell.getCellType());
+            assertEquals("SUM(A1:A5)", formulaCell.getCellFormula());
+
+            // Verify bold style is preserved on formula cell
+            Font font = 
workbook.getFontAt(formulaCell.getCellStyle().getFontIndex());
+            assertTrue(font.getBold(), "Formula cell should still be bold");
+        }
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to