Repository: wangguanquan/eec
Pull request #341 (2023-03-17 10:18:44)

Fix#337.1 Column添加auto-size属性
#337


Patch:
diff --git a/src/main/java/org/ttzero/excel/annotation/ExcelColumn.java b/src/main/java/org/ttzero/excel/annotation/ExcelColumn.java
index 25717712..5742b381 100644
--- a/src/main/java/org/ttzero/excel/annotation/ExcelColumn.java
+++ b/src/main/java/org/ttzero/excel/annotation/ExcelColumn.java
@@ -102,11 +102,11 @@ public @interface ExcelColumn {
 
     /**
      * If {@link Sheet#autoSize()} is {@code true}, The column width take the minimum of `width` and `maxWidth`,
-     * if `autoWidth` is {@code false}, The column width use `maxWidth` directly as the column width
+     * otherwise the column width use `maxWidth` directly as the column width
      *
-     * @return max cell width, less than or equal to 0 means unset
+     * @return max cell width, negative number means unset
      */
-    double maxWidth() default 0.0D;
+    double maxWidth() default -1D;
 
     /**
      * Hidden current column
diff --git a/src/main/java/org/ttzero/excel/entity/Column.java b/src/main/java/org/ttzero/excel/entity/Column.java
index 02b6f16b..8bcad495 100644
--- a/src/main/java/org/ttzero/excel/entity/Column.java
+++ b/src/main/java/org/ttzero/excel/entity/Column.java
@@ -66,10 +66,10 @@ public class Column {
      * The cell type
      */
     public Class<?> clazz;
-    /**
-     * The string value is shared
-     */
-    public boolean share;
+//    /**
+//     * The string value is shared
+//     */
+//    public boolean share;
     /**
      * The int value conversion
      */
@@ -97,7 +97,7 @@ public class Column {
     /**
      * The cell width and height
      */
-    public double width, headerHeight = -1D;
+    public double width = -1D, headerHeight = -1D;
     public double o;
     public Styles styles;
     public Comment headerComment, cellComment;
@@ -105,14 +105,14 @@ public class Column {
      * Specify the cell number format
      */
     public NumFmt numFmt;
-    /**
-     * Only export column name and ignore value
-     */
-    public boolean ignoreValue;
-    /**
-     * Wrap text in a cell
-     */
-    public int wrapText;
+//    /**
+//     * Only export column name and ignore value
+//     */
+//    public boolean ignoreValue;
+//    /**
+//     * Wrap text in a cell
+//     */
+//    public int wrapText;
     /**
      * Specify the column index
      */
@@ -145,13 +145,27 @@ public class Column {
      * The real col-Index used to write
      */
     public int realColIndex;
+//    /**
+//     * Hidden current column
+//     * <p>
+//     * Only set the column to hide, the data will still be written,
+//     * you can right-click to "un-hide" to display in file
+//     */
+//    public boolean hide;
     /**
-     * Hidden current column
-     * <p>
-     * Only set the column to hide, the data will still be written,
-     * you can right-click to "un-hide" to display in file
+     * Simple properties
+     *
+     * <blockquote><pre>
+     *  Bit  | Contents
+     * ------+---------
+     * 31. 1 | Warp Text
+     * 30. 2 | Auto size, 0: default, 1: auto-size 2: fixed-size
+     * 28. 1 | Ignore Value, Only export column name
+     * 27. 1 | Hide column
+     * 26. 1 | Shared String
+     * </pre></blockquote>
      */
-    public boolean hide;
+    public int option;
 
     /**
      * Constructor Column
@@ -231,7 +245,7 @@ public class Column {
     public Column(String name, Class<?> clazz, boolean share) {
         this.name = name;
         this.clazz = clazz;
-        this.share = share;
+        setShare(share);
     }
 
     /**
@@ -244,7 +258,7 @@ public class Column {
     public Column(String name, String key, boolean share) {
         this.name = name;
         this.key = key;
-        this.share = share;
+        setShare(share);
     }
 
     /**
@@ -354,7 +368,7 @@ public class Column {
         this.key = other.key;
         this.name = other.name;
         this.clazz = other.clazz;
-        this.share = other.share;
+//        this.share = other.share;
         this.processor = other.processor;
         this.styleProcessor = other.styleProcessor;
         this.width = other.width;
@@ -364,10 +378,11 @@ public class Column {
         this.headerComment = other.headerComment;
         this.cellComment = other.cellComment;
         this.numFmt = other.numFmt;
-        this.ignoreValue = other.ignoreValue;
-        this.wrapText = other.wrapText;
+//        this.ignoreValue = other.ignoreValue;
+//        this.wrapText = other.wrapText;
         this.colIndex = other.colIndex;
-        this.hide = other.hide;
+//        this.hide = other.hide;
+        this.option = other.option;
         this.realColIndex = other.realColIndex;
         if (other.cellStyle != null) setCellStyle(other.cellStyle);
         if (other.headerStyle != null) setHeaderStyle(other.headerStyle);
@@ -411,7 +426,7 @@ public class Column {
      * @return true:shared false:inline string
      */
     public boolean isShare() {
-        return share;
+        return (option >> 5 & 1) == 1;
     }
 
     /**
@@ -712,7 +727,8 @@ public class Column {
      * @return the {@link Column}
      */
     public Column setShare(boolean share) {
-        this.share = share;
+        if (share) this.option |= 1 << 5;
+        else this.option &= ~(1 << 5);
         return this;
     }
 
@@ -781,7 +797,7 @@ public class Column {
             style = Styles.clearNumFmt(style) | styles.addNumFmt(numFmt);
         }
 
-        return style | wrapText;
+        return style | (option & 1);
     }
 
     /**
@@ -801,7 +817,7 @@ public class Column {
      * @return bool
      */
     public boolean isIgnoreValue() {
-        return ignoreValue;
+        return (option >> 3 & 1) == 1;
     }
 
     /**
@@ -810,7 +826,7 @@ public class Column {
      * @return the {@link Column} self
      */
     public Column ignoreValue() {
-        this.ignoreValue = true;
+        this.option |= 1 << 3;
         return this;
     }
 
@@ -824,7 +840,8 @@ public class Column {
      * @return the {@link Column} self
      */
     public Column setWrapText(boolean wrapText) {
-        this.wrapText = wrapText ? 1 : 0;
+        if (wrapText) this.option |= 1;
+        else this.option = option >>> 1 << 1;
         return this;
     }
 
@@ -933,7 +950,7 @@ public class Column {
      * @return true: hidden otherwise show
      */
     public boolean isHide() {
-        return hide;
+        return (option >> 4 & 1) == 1;
     }
 
     /**
@@ -942,7 +959,7 @@ public class Column {
      * @return current {@link Column}
      */
     public Column hide() {
-        this.hide = true;
+        this.option |= 1 << 4;
         return this;
     }
 
@@ -952,7 +969,7 @@ public class Column {
      * @return current {@link Column}
      */
     public Column show() {
-        this.hide = false;
+        this.option &= ~(1 << 4);
         return this;
     }
 
@@ -964,4 +981,45 @@ public class Column {
     public Column getTail() {
         return tail != null ? tail : this;
     }
+
+    /**
+     * Setting auto resize cell's width
+     *
+     * @return current {@link Column}
+     */
+    public Column autoSize() {
+        this.option |= 1 << 1;
+        return this;
+    }
+
+    /**
+     * Setting fix column width
+     *
+     * @return current {@link Column}
+     */
+    public Column fixedSize() {
+        this.option |= 1 << 2;
+        return this;
+    }
+
+    /**
+     * Setting fixed column width
+     *
+     * @param width the column width
+     * @return current {@link Column}
+     */
+    public Column fixedSize(double width) {
+        this.option |= 1 << 2;
+        this.width = width;
+        return this;
+    }
+
+    /**
+     * Returns the re-size setting
+     *
+     * @return 0: not setting 1: auto-size 2:fixed-size
+     */
+    public int getAutoSize() {
+        return option >> 1 & 3;
+    }
 }
diff --git a/src/main/java/org/ttzero/excel/entity/ListSheet.java b/src/main/java/org/ttzero/excel/entity/ListSheet.java
index 16577e17..a90dd0bf 100644
--- a/src/main/java/org/ttzero/excel/entity/ListSheet.java
+++ b/src/main/java/org/ttzero/excel/entity/ListSheet.java
@@ -648,7 +648,7 @@ public class ListSheet<T> extends Sheet {
         // Hidden Column
         if (ec.hide()) column.hide();
         // Cell max width
-        column.width = ec.maxWidth();
+        if (ec.maxWidth() >= 0.0D) column.width = ec.maxWidth();
         return column;
     }
 
diff --git a/src/main/java/org/ttzero/excel/entity/RowBlock.java b/src/main/java/org/ttzero/excel/entity/RowBlock.java
index 4c74631b..d9237260 100644
--- a/src/main/java/org/ttzero/excel/entity/RowBlock.java
+++ b/src/main/java/org/ttzero/excel/entity/RowBlock.java
@@ -60,7 +60,7 @@ public class RowBlock implements Iterator<Row> {
      */
     public final RowBlock reopen() {
         eof = false;
-//        total = 0;
+        total = 0;
         return this;
     }
 
diff --git a/src/main/java/org/ttzero/excel/entity/Sheet.java b/src/main/java/org/ttzero/excel/entity/Sheet.java
index fb739024..83af329c 100644
--- a/src/main/java/org/ttzero/excel/entity/Sheet.java
+++ b/src/main/java/org/ttzero/excel/entity/Sheet.java
@@ -109,7 +109,7 @@ public abstract class Sheet implements Cloneable, Storable {
     /**
      * The default cell width
      */
-    protected double width = 20.38D;
+    protected double width = 15.5D;
     /**
      * The row number
      */
@@ -438,7 +438,9 @@ public abstract class Sheet implements Cloneable, Storable {
      * Setting fix column width
      *
      * @return current {@link Sheet}
+     * @deprecated rename to {@link #fixedSize()}
      */
+    @Deprecated
     public Sheet fixSize() {
         this.autoSize = 2;
         return this;
@@ -449,16 +451,11 @@ public abstract class Sheet implements Cloneable, Storable {
      *
      * @param width the column width
      * @return current {@link Sheet}
+     * @deprecated rename to {@link #fixedSize(double)}
      */
+    @Deprecated
     public Sheet fixSize(double width) {
-        this.autoSize = 2;
-        this.width = width;
-        if (headerReady) {
-            for (org.ttzero.excel.entity.Column hc : columns) {
-                hc.setWidth(width);
-            }
-        }
-        return this;
+        return fixedSize(width);
     }
 
     /**
@@ -479,6 +476,42 @@ public abstract class Sheet implements Cloneable, Storable {
         return autoSize == 1;
     }
 
+    /**
+     * Setting fixed column width
+     *
+     * @return current {@link Sheet}
+     */
+    public Sheet fixedSize() {
+        this.autoSize = 2;
+        return this;
+    }
+
+    /**
+     * Setting fix column width
+     *
+     * @param width the column width
+     * @return current {@link Sheet}
+     */
+    public Sheet fixedSize(double width) {
+        if (width < 0.0D) {
+            LOGGER.warn("Negative number {}", width);
+            width = 0.0D;
+        }
+        else if (width > Const.Limit.COLUMN_WIDTH) {
+            LOGGER.warn("Maximum width is {}, current is {}", Const.Limit.COLUMN_WIDTH, width);
+            width = Const.Limit.COLUMN_WIDTH;
+        }
+        this.autoSize = 2;
+        this.width = width;
+        if (headerReady) {
+            for (org.ttzero.excel.entity.Column hc : columns) {
+                hc.fixedSize(width);
+            }
+        }
+        return this;
+    }
+
+
     /**
      * Cancel the odd row's fill style
      *
@@ -731,6 +764,11 @@ public abstract class Sheet implements Cloneable, Storable {
                 for (org.ttzero.excel.entity.Column col = column.next; col != null; col = col.next)
                     col.styles = workbook.getStyles();
             }
+
+            // Column width
+            if (column.getAutoSize() == 0 && autoSize > 0) {
+                column.option |= autoSize << 1;
+            }
         }
     }
 
diff --git a/src/main/java/org/ttzero/excel/entity/e7/XMLWorkbookWriter.java b/src/main/java/org/ttzero/excel/entity/e7/XMLWorkbookWriter.java
index 38980164..7bd20e23 100644
--- a/src/main/java/org/ttzero/excel/entity/e7/XMLWorkbookWriter.java
+++ b/src/main/java/org/ttzero/excel/entity/e7/XMLWorkbookWriter.java
@@ -374,7 +374,7 @@ public class XMLWorkbookWriter implements IWorkbookWriter {
                 if (workbook.isAutoSize()) {
                     sheet.autoSize();
                 } else {
-                    sheet.fixSize();
+                    sheet.fixedSize();
                 }
             }
             if (sheet.getAutoOdd() == -1) {
diff --git a/src/main/java/org/ttzero/excel/entity/e7/XMLWorksheetWriter.java b/src/main/java/org/ttzero/excel/entity/e7/XMLWorksheetWriter.java
index 97ba98e6..40f3c5ae 100644
--- a/src/main/java/org/ttzero/excel/entity/e7/XMLWorksheetWriter.java
+++ b/src/main/java/org/ttzero/excel/entity/e7/XMLWorksheetWriter.java
@@ -42,6 +42,7 @@ import java.io.File;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
+import java.io.OutputStreamWriter;
 import java.lang.reflect.InvocationTargetException;
 import java.math.BigDecimal;
 import java.nio.ByteBuffer;
@@ -97,8 +98,11 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
     protected Column[] columns;
     protected SharedStrings sst;
     protected Comments comments;
-    protected int startRow;
-    protected long pStart, pEnd; // The position dimension to sheetData
+    protected int startRow, totalRows;
+    /**
+     * If there are any auto-width columns
+     */
+    protected boolean includeAutoWidth;
 
     public XMLWorksheetWriter() { }
 
@@ -123,37 +127,31 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
         // write before
         writeBefore();
 
+        // Write body data
+        beforeSheetData(sheet.getNonHeader() == 1);
+
         if (rowBlock != null && rowBlock.hasNext()) {
-            if (sheet.isAutoSize()) {
-                do {
-                    // write row-block data auto size
-                    writeAutoSizeRowBlock(rowBlock);
-                    // end of row
-                    if (rowBlock.isEOF()) break;
-                } while ((rowBlock = supplier.get()) != null);
-            } else {
-                do {
-                    // write row-block data
-                    writeRowBlock(rowBlock);
-                    // end of row
-                    if (rowBlock.isEOF()) break;
-                } while ((rowBlock = supplier.get()) != null);
-            }
+            do {
+                // write row-block data
+                writeRowBlock(rowBlock);
+                // end of row
+                if (rowBlock.isEOF()) break;
+            } while ((rowBlock = supplier.get()) != null);
         }
 
-        int total = rowBlock != null ? rowBlock.getTotal() : 0;
+        totalRows = rowBlock != null ? rowBlock.getTotal() : 0;
 
         // write end
-        writeAfter(total);
+        writeAfter(totalRows);
 
         // Write some final info
         sheet.afterSheetAccess(workSheetPath);
 
-        // resize
-        if (sheet.isAutoSize()) {
+        // Resize if include auto-width column
+        if (includeAutoWidth) {
             // close writer before resize
             close();
-            resizeColumnWidth(sheetPath.toFile(), total);
+            resizeColumnWidth(sheetPath.toFile(), totalRows);
         }
     }
 
@@ -173,39 +171,33 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
         // write before
         writeBefore();
 
+        // Write body data
+        beforeSheetData(sheet.getNonHeader() == 1);
+
         if (rowBlock.hasNext()) {
-            if (sheet.isAutoSize()) {
-                for (; ; ) {
-                    // write row-block data
-                    writeAutoSizeRowBlock(rowBlock);
-                    // end of row
-                    if (rowBlock.isEOF()) break;
-                    // Get the next block
-                    rowBlock = sheet.nextBlock();
-                }
-            } else {
-                for (; ; ) {
-                    // write row-block data
-                    writeRowBlock(rowBlock);
-                    // end of row
-                    if (rowBlock.isEOF()) break;
-                    // Get the next block
-                    rowBlock = sheet.nextBlock();
-                }
+            for (; ; ) {
+                // write row-block data
+                writeRowBlock(rowBlock);
+                // end of row
+                if (rowBlock.isEOF()) break;
+                // Get the next block
+                rowBlock = sheet.nextBlock();
             }
         }
 
+        totalRows = rowBlock.getTotal();
+
         // write end
-        writeAfter(rowBlock.getTotal());
+        writeAfter(totalRows);
 
         // Write some final info
         sheet.afterSheetAccess(workSheetPath);
 
-        // resize
-        if (sheet.isAutoSize()) {
-            // close writer before resize
+        // Resize if include auto-width column
+        if (includeAutoWidth) {
+            // Close writer before resize
             close();
-            resizeColumnWidth(sheetPath.toFile(), rowBlock.getTotal());
+            resizeColumnWidth(sheetPath.toFile(), totalRows);
         }
     }
 
@@ -316,7 +308,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
 
         // Default row height and width
         int fillSpace = 17; // Column width xxx.xx (6byte) + hidden property (11byte)
-        BigDecimal width = BigDecimal.valueOf(!nonHeader ? sheet.getDefaultWidth() : 8.38D);
+        BigDecimal width = BigDecimal.valueOf(!nonHeader ? sheet.getDefaultWidth() : 15.5D).add(new BigDecimal("0.65"));
         // Overflow column width limit
         if (width.compareTo(new BigDecimal(Const.Limit.COLUMN_WIDTH)) > 0) {
             width = new BigDecimal(Const.Limit.COLUMN_WIDTH);
@@ -327,8 +319,6 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
         // cols
         writeCols(fillSpace, defaultWidth);
 
-        // Write body data
-        beforeSheetData(nonHeader);
     }
 
     /**
@@ -368,18 +358,10 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
             bw.write("\">");
 
             String name;
-            if (sheet.isAutoSize()) {
-                for (int j = 0, c = 0; j < columns.length; j++) {
-                    Column hc = columnsArray[j][i];
-                    name = isNotEmpty(hc.getName()) ? hc.getName() : mergedGrid != null && mergedGrid.test(i + 1, hc.getRealColIndex()) && !isFirstMergedCell(mergeCells, i + 1, hc.getRealColIndex()) ? null : hc.key;
-                    writeStringAutoSize(name, row, c++, hc.getHeaderStyleIndex() == -1 ? defaultStyleIndex : hc.getHeaderStyleIndex());
-                }
-            } else {
-                for (int j = 0, c = 0; j < columns.length; j++) {
-                    Column hc = columnsArray[j][i];
-                    name = isNotEmpty(hc.getName()) ? hc.getName() : mergedGrid != null && mergedGrid.test(i + 1, hc.getRealColIndex()) && !isFirstMergedCell(mergeCells, i + 1, hc.getRealColIndex()) ? null : hc.key;
-                    writeString(name, row, c++, hc.getHeaderStyleIndex() == -1 ? defaultStyleIndex : hc.getHeaderStyleIndex());
-                }
+            for (int j = 0, c = 0; j < columns.length; j++) {
+                Column hc = columnsArray[j][i];
+                name = isNotEmpty(hc.getName()) ? hc.getName() : mergedGrid != null && mergedGrid.test(i + 1, hc.getRealColIndex()) && !isFirstMergedCell(mergeCells, i + 1, hc.getRealColIndex()) ? null : hc.key;
+                writeString(name, row, c++, hc.getHeaderStyleIndex() == -1 ? defaultStyleIndex : hc.getHeaderStyleIndex());
             }
 
             // Write header comments
@@ -428,7 +410,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
      *
      * @param rowBlock the row-block
      */
-    private void writeRowBlock(RowBlock rowBlock) throws IOException {
+    protected void writeRowBlock(RowBlock rowBlock) throws IOException {
         for (; rowBlock.hasNext(); writeRow(rowBlock.next())) ;
     }
 
@@ -437,6 +419,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
      *
      * @param rowBlock the row-block
      */
+    @Deprecated
     private void writeAutoSizeRowBlock(RowBlock rowBlock) throws IOException {
         for (; rowBlock.hasNext(); writeRowAutoSize(rowBlock.next())) ;
     }
@@ -553,6 +536,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
      * @param row a row data
      * @throws IOException if I/O error occur
      */
+    @Deprecated
     protected void writeRowAutoSize(Row row) throws IOException {
         Cell[] cells = row.getCells();
         int len = cells.length, r = startRow(row.getIndex(), len, row.getHeight());
@@ -633,6 +617,12 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
             bw.escapeWrite(s); // escape text
             bw.write("</t></is></c>");
         }
+
+        // TODO optimize If auto-width
+        if (hc.getAutoSize() == 1) {
+            double ln;
+            if (hc.o < (ln = stringWidth(s, xf))) hc.o = ln;
+        }
     }
 
     /**
@@ -644,13 +634,14 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
      * @param xf     the style index
      * @throws IOException if I/O error occur
      */
+    @Deprecated
     protected void writeStringAutoSize(String s, int row, int column, int xf) throws IOException {
         writeString(s, row, column, xf);
-        Column hc = columns[column];
-        double ln;
-        if (hc.o < (ln = stringWidth(s, xf))) {
-            hc.o = ln;
-        }
+//        Column hc = columns[column];
+//        double ln;
+//        if (hc.o < (ln = stringWidth(s, xf))) {
+//            hc.o = ln;
+//        }
     }
 
     /**
@@ -663,14 +654,21 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
      * @throws IOException if I/O error occur
      */
     protected void writeDouble(double d, int row, int column, int xf) throws IOException {
+        Column hc = columns[column];
         bw.write("<c r=\"");
-        bw.write(int2Col(columns[column].getRealColIndex()));
+        bw.write(int2Col(hc.getRealColIndex()));
         bw.writeInt(row);
         bw.write("\" s=\"");
         bw.writeInt(xf);
         bw.write("\"><v>");
         bw.write(d);
         bw.write("</v></c>");
+
+        // TODO optimize If auto-width
+        if (hc.getAutoSize() == 1) {
+            int n;
+            if (hc.o < (n = Double.toString(d).length())) hc.o = n;
+        }
     }
 
     /**
@@ -682,13 +680,14 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
      * @param xf     the style index
      * @throws IOException if I/O error occur
      */
+    @Deprecated
     protected void writeDoubleAutoSize(double d, int row, int column, int xf) throws IOException {
         writeDouble(d, row, column, xf);
-        Column hc = columns[column];
-        int n;
-        if (hc.width == 0 && hc.o < (n = Double.toString(d).length())) {
-            hc.o = n;
-        }
+//        Column hc = columns[column];
+//        int n;
+//        if (hc.width == 0 && hc.o < (n = Double.toString(d).length())) {
+//            hc.o = n;
+//        }
     }
 
     /**
@@ -701,14 +700,20 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
      * @throws IOException if I/O error occur
      */
     protected void writeDecimal(BigDecimal bd, int row, int column, int xf) throws IOException {
+        Column hc = columns[column];
         bw.write("<c r=\"");
-        bw.write(int2Col(columns[column].getRealColIndex()));
+        bw.write(int2Col(hc.getRealColIndex()));
         bw.writeInt(row);
         bw.write("\" s=\"");
         bw.writeInt(xf);
         bw.write("\"><v>");
         bw.write(bd.toString());
         bw.write("</v></c>");
+        // TODO optimize If auto-width
+        if (hc.getAutoSize() == 1) {
+            int l;
+            if (hc.o < (l = bd.toString().length())) hc.o = l;
+        }
     }
 
     /**
@@ -720,13 +725,14 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
      * @param xf     the style index
      * @throws IOException if I/O error occur
      */
+    @Deprecated
     protected void writeDecimalAutoSize(BigDecimal bd, int row, int column, int xf) throws IOException {
         writeDecimal(bd, row, column, xf);
-        Column hc = columns[column];
-        int l;
-        if (hc.width == 0 && hc.o < (l = bd.toString().length())) {
-            hc.o = l;
-        }
+//        Column hc = columns[column];
+//        int l;
+//        if (hc.width == 0 && hc.o < (l = bd.toString().length())) {
+//            hc.o = l;
+//        }
     }
 
     /**
@@ -759,14 +765,20 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
      * @throws IOException if I/O error occur
      */
     protected void writeNumeric(long l, int row, int column, int xf) throws IOException {
+        Column hc = columns[column];
         bw.write("<c r=\"");
-        bw.write(int2Col(columns[column].getRealColIndex()));
+        bw.write(int2Col(hc.getRealColIndex()));
         bw.writeInt(row);
         bw.write("\" s=\"");
         bw.writeInt(xf);
         bw.write("\"><v>");
         bw.write(l);
         bw.write("</v></c>");
+        // TODO optimize If auto-width
+        if (hc.getAutoSize() == 1) {
+            int n;
+            if (hc.o < (n = stringSize(l))) hc.o = n;
+        }
     }
 
     /**
@@ -778,13 +790,14 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
      * @param xf     the style index
      * @throws IOException if I/O error occur
      */
+    @Deprecated
     protected void writeNumericAutoSize(long l, int row, int column, int xf) throws IOException {
         writeNumeric(l, row, column, xf);
-        Column hc = columns[column];
-        int n;
-        if (hc.width == 0 && hc.o < (n = stringSize(l))) {
-            hc.o = n;
-        }
+//        Column hc = columns[column];
+//        int n;
+//        if (hc.width == 0 && hc.o < (n = stringSize(l))) {
+//            hc.o = n;
+//        }
     }
 
     /**
@@ -834,10 +847,18 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
     protected void resizeColumnWidth(File path, int rows) throws IOException {
         // There has no column to reset width
         if (columns.length <= 0 || rows <= 0) return;
-        String[] widths = new String[columns.length];
+//        String[] widths = new String[columns.length];
         // Collect column width
         for (int i = 0; i < columns.length; i++) {
             Column hc = columns[i];
+            int k = hc.getAutoSize();
+            // If fixed width
+            if (k == 2) {
+                double width = hc.width >= 0.0D ? hc.width: sheet.getDefaultWidth();
+//                widths[i] = BigDecimal.valueOf(Math.min(width + 0.65D, Const.Limit.COLUMN_WIDTH)).setScale(2, BigDecimal.ROUND_HALF_UP).toString();
+                hc.width = BigDecimal.valueOf(Math.min(width + 0.65D, Const.Limit.COLUMN_WIDTH)).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
+                continue;
+            }
             double _l = stringWidth(hc.name, hc.getCellStyleIndex()), len;
             Class<?> clazz = hc.getClazz();
             if (isString(clazz)) {
@@ -888,83 +909,29 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
                 len = hc.o > 0 ? hc.o : 10.0D;
             }
             double width = Math.max(_l, len) + 1.86D;
-            if (hc.width > 0.000001D) width = Math.min(width, hc.width);
+            if (hc.width > 0.000001D) width = Math.min(width, hc.width + 0.65D);
             if (width > Const.Limit.COLUMN_WIDTH) {
                 width = Const.Limit.COLUMN_WIDTH;
             }
-            widths[i] = BigDecimal.valueOf(width).setScale(2, BigDecimal.ROUND_HALF_UP).toString();
+//            widths[i] = BigDecimal.valueOf(width).setScale(2, BigDecimal.ROUND_HALF_UP).toString();
+            hc.width = BigDecimal.valueOf(width).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
         }
-        // resize each column width ...
-        try (SeekableByteChannel channel = Files.newByteChannel(path.toPath(), StandardOpenOption.WRITE, StandardOpenOption.READ)) {
-            long[] offset = findHeaderOffset(channel);
-            if (((int) offset[1]) <= 0) return;
-            ByteBuffer buffer = ByteBuffer.allocate((int) offset[1]);
-            buffer.order(ByteOrder.LITTLE_ENDIAN);
-            channel.position(offset[0]);
-            int n = channel.read(buffer);
-            if (n < 0) {
-                throw new ExcelWriteException("Write worksheet [" + sheet.getName() + "] error.");
-            }
-            // Ready to read
-            buffer.flip();
-
-            // Rewrite dimension
-            int position = findPosition(buffer, "<dimension ");
-            // Get it
-            if (position > 0) {
-                buffer.put("ref=\"A1".getBytes(StandardCharsets.US_ASCII));
-                int fill = 11; // fill 11 space
-                buffer.put((byte) ':');
-                fill--;
-                char[] col = int2Col(columns[columns.length - 1].getRealColIndex());
-                buffer.put((new String(col) + (rows + 1)).getBytes(StandardCharsets.US_ASCII));
-                fill -= col.length;
-                fill -= stringSize(rows + 1);
-                buffer.put((byte) '"');
-                for (; fill-->0;) buffer.put((byte) 32); // Fill space
-            }
 
-            // Rewrite cols
-            position = findPosition(buffer, "<cols>");
-            if (position > 0) {
-                for (int i = 0; i < columns.length; i++) {
-                    String s = widths[i];
-                    position = findPosition(buffer, "width=\"");
-                    if (position == -1) continue;
-                    buffer.put(s.getBytes(StandardCharsets.US_ASCII));
-                    buffer.put((byte) '"');
-                    int fillSpace = 17;
-                    if (columns[i].isHide()) {
-                        buffer.put(" hidden=\"1\"".getBytes(StandardCharsets.US_ASCII));
-                        fillSpace -= 11;
-                    }
-                    for (int j = fillSpace - s.length(); j-- > 0; ) buffer.put((byte) 32); // Fill space
-                }
-            }
+        XMLWorksheetWriter _writer = new XMLWorksheetWriter(sheet);
+        _writer.totalRows = totalRows;
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        _writer.bw = new ExtBufferedWriter(new OutputStreamWriter(baos, StandardCharsets.UTF_8));
+        _writer.writeBefore();
+        _writer.bw.close();
 
-            // Ready to write
-            buffer.position(n);
-            buffer.flip();
-            // Move to header
-            channel.position(offset[0]);
+        // Resize each column width ...
+        try (SeekableByteChannel channel = Files.newByteChannel(path.toPath(), StandardOpenOption.WRITE, StandardOpenOption.READ)) {
+            ByteBuffer buffer = ByteBuffer.wrap(baos.toByteArray());
+            buffer.order(ByteOrder.LITTLE_ENDIAN);
             channel.write(buffer);
         }
     }
 
-    private int findPosition(ByteBuffer buffer, String key) {
-        byte[] values = key.getBytes(StandardCharsets.UTF_8);
-        for (; ; ) {
-            for (; buffer.hasRemaining() && buffer.get() != values[0]; );
-            if (!buffer.hasRemaining()) break;
-            int j = 1;
-            for (; j < values.length && buffer.hasRemaining() && buffer.get() == values[j++]; );
-            if (j == values.length) {
-                return 1;
-            }
-        }
-        return -1;
-    }
-
     /**
      * Release resources
      */
@@ -1009,17 +976,18 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
      * @throws IOException if I/O error occur.
      */
     protected void writeDimension() throws IOException {
-        pStart = bw.getWrittenChars();
         bw.append("<dimension ref=\"A1"); // FIXME Setting the column or row's start-index
-        int n = 11, size = sheet.size(); // fill 11 space
-        if (size > 0) {
+        int n = 11; // fill 11 space
+        if (totalRows > 0) {
             bw.write(':');
             n--;
-            char[] col = int2Col(columns[columns.length - 1].getRealColIndex());
+            Column hc = columns[columns.length - 1];
+            char[] col = int2Col(hc.getRealColIndex());
             bw.write(col);
             n -= col.length;
-            bw.writeInt(size + 1);
-            n -= stringSize(size + 1);
+            int t = totalRows + startRow + hc.subColumnSize();
+            bw.writeInt(t);
+            n -= stringSize(t);
         }
         bw.write('"');
         for (; n-->0;) bw.write(32); // Fill space
@@ -1085,7 +1053,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
      * Write the sheet format
      *
      * @param fillSpace The number of characters to pad when recalculating the width.
-     * @param defaultWidth The default cell width, {@code 8.38} will be use if it not be setting.
+     * @param defaultWidth The default cell width, {@code 15.5} will be use if it not be setting.
      * @throws IOException if I/O error occur.
      */
     protected void writeSheetFormat(int fillSpace, String defaultWidth) throws IOException {
@@ -1100,7 +1068,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
      * Write the default column info, The specified column width will be overwritten in these method.
      *
      * @param fillSpace The number of characters to pad when recalculating the width.
-     * @param defaultWidth The default cell width, {@code 8.38} will be use if it not be setting.
+     * @param defaultWidth The default cell width, {@code 15.5} will be use if it not be setting.
      * @throws IOException if I/O error occur.
      */
     protected void writeCols(int fillSpace, String defaultWidth) throws IOException {
@@ -1108,6 +1076,8 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
             bw.write("<cols>");
             for (int i = 0; i < columns.length; i++) {
                 Column col = columns[i];
+                // Mark auto-width
+                includeAutoWidth |= col.getAutoSize() == 1;
                 String width = col.width >= 0.0000001D ? new BigDecimal(col.width).setScale(2, BigDecimal.ROUND_HALF_UP).toString() : defaultWidth;
                 int w = width.length();
                 bw.write("<col customWidth=\"1\" width=\"");
@@ -1135,7 +1105,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
      * @throws IOException if I/O error occur.
      */
     protected void beforeSheetData(boolean nonHeader) throws IOException {
-        pEnd = bw.getWrittenChars();
+        // Start to write sheet data
         bw.write("<sheetData>");
 
         int headerRow = 1;
@@ -1242,54 +1212,6 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
         return false;
     }
 
-    protected long[] findHeaderOffset(SeekableByteChannel channel) throws IOException {
-        // The header data is all ascii characters, so the char length is used directly as the byte length
-        if (pEnd > 0) {
-            long start = Math.max(0L, pStart);
-            return new long[] { start, pEnd - start};
-        }
-
-        // From disk
-        long pos = channel.position(), position = 0L;
-        try {
-            ByteBuffer buffer = ByteBuffer.allocate(1 << 12);
-            channel.position(0L);
-            out: for (; channel.read(buffer) > 0; ) {
-                buffer.flip();
-                int i = 0, limit = buffer.remaining();
-
-                for (; ;) {
-                    for (; i < limit && buffer.get(i) != '<'; i++) ;
-                    // Overflow
-                    if (i >= limit) {
-                        position += i;
-                        buffer.clear();
-                        continue out;
-                    }
-                    // Incomplete key
-                    else if (i > limit - 10) {
-                        buffer.position(i);
-                        position += buffer.position();
-                        buffer.compact();
-                        continue out;
-                    }
-                    // Find <sheetData
-                    else if (buffer.get(i + 1) == 's' && buffer.get(i + 2) == 'h' && buffer.get(i + 3) == 'e'
-                        && buffer.get(i + 4) == 'e' && buffer.get(i + 5) == 't' && buffer.get(i + 6) == 'D'
-                        && buffer.get(i + 7) == 'a' && buffer.get(i + 8) == 't' && buffer.get(i + 9) == 'a') {
-                        position += i;
-                        break out;
-                    }
-                    i++;
-                }
-            }
-
-            return new long[] { 0, position };
-        } finally {
-            channel.position(pos);
-        }
-    }
-
     /**
      * Returns the maximum cell height
      *
diff --git a/src/main/java/org/ttzero/excel/util/ExtBufferedWriter.java b/src/main/java/org/ttzero/excel/util/ExtBufferedWriter.java
index cc7d47c9..aea940e4 100644
--- a/src/main/java/org/ttzero/excel/util/ExtBufferedWriter.java
+++ b/src/main/java/org/ttzero/excel/util/ExtBufferedWriter.java
@@ -27,7 +27,6 @@ import java.io.Writer;
  */
 public class ExtBufferedWriter extends BufferedWriter {
     private static final int defaultCharBufferSize = 8192;
-    private long cn; // Char length
 
     public ExtBufferedWriter(Writer out) {
         this(out, defaultCharBufferSize);
@@ -154,33 +153,6 @@ public class ExtBufferedWriter extends BufferedWriter {
         write(Double.toString(d));
     }
 
-    @Override
-    public void write(int c) throws IOException {
-        super.write(c);
-        cn++;
-    }
-
-    @Override
-    public void write(char[] cbuf, int off, int len) throws IOException {
-        super.write(cbuf, off, len);
-        cn += len;
-    }
-
-    @Override
-    public void write(String s, int off, int len) throws IOException {
-        super.write(s, off, len);
-        cn += len;
-    }
-
-    /**
-     * Returns the number of characters that have been written
-     *
-     * @return number of characters
-     */
-    public long getWrittenChars() {
-        return cn;
-    }
-
     public char[] toChars(int i) {
         if (i == Integer.MIN_VALUE)
             return MIN_INTEGER_CHARS;


Test patch:
diff --git a/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest.java b/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest.java
index cbbd6d79..2587b25b 100644
--- a/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest.java
+++ b/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest.java
@@ -1352,7 +1352,7 @@ public class ListObjectSheetTest extends WorkbookTest {
             new ListSheet<>(list).setStyleProcessor(new TemplateStyleProcessor())
                 .cancelOddStyle().ignoreHeader().putExtProp(Const.ExtendPropertyKey.MERGE_CELLS, Collections.singletonList(Dimension.of("A1:B1")))
                 .setSheetWriter(new XMLWorksheetWriter() {
-                    protected int startRow(int rows, int columns) throws IOException {
+                    protected int startRow(int rows, int columns, double rowHeight) throws IOException {
                         // Row number
                         int r = rows + startRow;
                         // logging
@@ -1537,7 +1537,7 @@ public class ListObjectSheetTest extends WorkbookTest {
         protected void writeRow(Row row) throws IOException {
             Cell[] cells = row.getCells();
             int len = cells.length, r = row.getIndex() / tile + startRow, c = columns[columns.length - 1].realColIndex / tile, y = row.getIndex() % tile;
-            if (y == 0) startRow(r - startRow, columns[columns.length - 1].realColIndex);
+            if (y == 0) startRow(r - startRow, columns[columns.length - 1].realColIndex, -1);
 
             for (int i = 0; i < len; i++) {
                 Cell cell = cells[i];
@@ -1579,52 +1579,6 @@ public class ListObjectSheetTest extends WorkbookTest {
                 bw.write("</row>");
         }
 
-        @Override
-        protected void writeRowAutoSize(Row row) throws IOException {
-            Cell[] cells = row.getCells();
-            int len = cells.length, r = row.getIndex() / tile + startRow, c = columns[columns.length - 1].realColIndex / tile, y = row.getIndex() % tile;
-            if (y == 0) startRow(r - startRow, columns[columns.length - 1].realColIndex);
-
-            for (int i = 0; i < len; i++) {
-                Cell cell = cells[i];
-                int xf = cell.xf, col = i + c * y;
-                switch (cell.t) {
-                    case INLINESTR:
-                    case SST:
-                        writeStringAutoSize(cell.sv, r, col, xf);
-                        break;
-                    case NUMERIC:
-                        writeNumericAutoSize(cell.nv, r, col, xf);
-                        break;
-                    case LONG:
-                        writeNumericAutoSize(cell.lv, r, col, xf);
-                        break;
-                    case DATE:
-                    case DATETIME:
-                    case DOUBLE:
-                    case TIME:
-                        writeDoubleAutoSize(cell.dv, r, col, xf);
-                        break;
-                    case BOOL:
-                        writeBool(cell.bv, r, col, xf);
-                        break;
-                    case DECIMAL:
-                        writeDecimalAutoSize(cell.mv, r, col, xf);
-                        break;
-                    case CHARACTER:
-                        writeChar(cell.cv, r, col, xf);
-                        break;
-                    case BLANK:
-                        writeNull(r, col, xf);
-                        break;
-                    default:
-                }
-            }
-            // 注意这里可能不会关闭row需要在writeAfter进行二次处理
-            if (y == tile - 1)
-                bw.write("</row>");
-        }
-
         @Override
         protected void writeAfter(int total) throws IOException {
             if (total > 0 && (total - 1) % tile < tile - 1) bw.write("</row>");
diff --git a/src/test/java/org/ttzero/excel/entity/StatementSheetTest.java b/src/test/java/org/ttzero/excel/entity/StatementSheetTest.java
index 8a6f07d6..8b23522f 100644
--- a/src/test/java/org/ttzero/excel/entity/StatementSheetTest.java
+++ b/src/test/java/org/ttzero/excel/entity/StatementSheetTest.java
@@ -319,7 +319,7 @@ public class StatementSheetTest extends SQLWorkbookTest {
         try (Connection con = getConnection()) {
             new Workbook("test statement fix width", author)
                 .watch(Print::println)
-                .addSheet(new StatementSheet(con, "select id, name, age, create_date, update_date from student limit 10").fixSize(10))
+                .addSheet(new StatementSheet(con, "select id, name, age, create_date, update_date from student limit 10").fixedSize(10))
                 .writeTo(defaultTestPath);
         }
     }
diff --git a/src/test/java/org/ttzero/excel/entity/csv/StatementSheetTest.java b/src/test/java/org/ttzero/excel/entity/csv/StatementSheetTest.java
index 43a8eeca..ab6f76ff 100644
--- a/src/test/java/org/ttzero/excel/entity/csv/StatementSheetTest.java
+++ b/src/test/java/org/ttzero/excel/entity/csv/StatementSheetTest.java
@@ -295,7 +295,7 @@ public class StatementSheetTest extends SQLWorkbookTest {
         try (Connection con = getConnection()) {
             new Workbook("test statement fix width")
                 .watch(Print::println)
-                .addSheet(new StatementSheet(con, "select id, name, age from student limit 10").fixSize(10))
+                .addSheet(new StatementSheet(con, "select id, name, age from student limit 10").fixedSize(10))
                 .saveAsCSV()
                 .writeTo(getOutputTestPath());
         }