Repository: wangguanquan/eec
Pull request #331 (2023-02-07 12:46:15)

Fix#330 Fix reset column width error


Patch:
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 041a25cf..2d8f79f6 100644
--- a/src/main/java/org/ttzero/excel/entity/e7/XMLWorksheetWriter.java
+++ b/src/main/java/org/ttzero/excel/entity/e7/XMLWorksheetWriter.java
@@ -98,6 +98,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
     protected SharedStrings sst;
     protected Comments comments;
     protected int startRow;
+    protected long pStart, pEnd; // The position dimension to sheetData
 
     public XMLWorksheetWriter() { }
 
@@ -793,7 +794,7 @@ 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 <= 1) return;
+        if (columns.length <= 0 || rows <= 0) return;
         String[] widths = new String[columns.length];
         // Collect column width
         for (int i = 0; i < columns.length; i++) {
@@ -856,9 +857,11 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
         }
         // resize each column width ...
         try (SeekableByteChannel channel = Files.newByteChannel(path.toPath(), StandardOpenOption.WRITE, StandardOpenOption.READ)) {
-            ByteBuffer buffer = ByteBuffer.allocate(316 + columns.length * 80);
+            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.");
@@ -904,7 +907,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
             buffer.position(n);
             buffer.flip();
             // Move to header
-            channel.position(0);
+            channel.position(offset[0]);
             channel.write(buffer);
         }
     }
@@ -967,6 +970,7 @@ 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) {
@@ -1065,14 +1069,14 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
             bw.write("<cols>");
             for (int i = 0; i < columns.length; i++) {
                 Column col = columns[i];
-                int w = defaultWidth.length();
+                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=\"");
+                bw.write(width);
                 if (col.isHide()) {
-                    bw.write(defaultWidth);
                     bw.write("\" hidden=\"1");
                     w += 11;
                 }
-                else bw.write(col.width > 0.0000001D ? new BigDecimal(col.width).setScale(2, BigDecimal.ROUND_HALF_UP).toString() : defaultWidth);
                 bw.write('"');
                 for (int j = fillSpace - w; j-- > 0; ) bw.write(32); // Fill space
                 bw.write(" min=\"");
@@ -1092,6 +1096,7 @@ public class XMLWorksheetWriter implements IWorksheetWriter {
      * @throws IOException if I/O error occur.
      */
     protected void beforeSheetData(boolean nonHeader) throws IOException {
+        pEnd = bw.getWrittenChars();
         bw.write("<sheetData>");
 
         int headerRow = 1;
@@ -1198,4 +1203,51 @@ 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);
+        }
+    }
 }
diff --git a/src/main/java/org/ttzero/excel/util/ExtBufferedWriter.java b/src/main/java/org/ttzero/excel/util/ExtBufferedWriter.java
index d07af29a..e4a39c1f 100644
--- a/src/main/java/org/ttzero/excel/util/ExtBufferedWriter.java
+++ b/src/main/java/org/ttzero/excel/util/ExtBufferedWriter.java
@@ -27,6 +27,7 @@ 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);
@@ -153,6 +154,33 @@ 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;
+    }
+
     private 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/AutoSizeTest.java b/src/test/java/org/ttzero/excel/entity/AutoSizeTest.java
new file mode 100644
index 00000000..69949c98
--- /dev/null
+++ b/src/test/java/org/ttzero/excel/entity/AutoSizeTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2017-2023, guanquan.wang@yandex.com All Rights Reserved.
+ *
+ * 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.ttzero.excel.entity;
+
+import org.junit.Test;
+import org.ttzero.excel.annotation.ExcelColumn;
+import org.ttzero.excel.annotation.FreezePanes;
+import org.ttzero.excel.manager.Const;
+
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author guanquan.wang at 2023-02-04 22:15
+ */
+public class AutoSizeTest extends WorkbookTest {
+
+    @Test public void testAutoSize() throws IOException {
+        List<ServerReport> reports = new ArrayList<>(2);
+        for (int i = 0; i < 2; i++) {
+            ServerReport e = new ServerReport();
+            e.index = i + 1;
+            e.clientRequestNum = i;
+            e.name = "测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试";
+            e.duration = 12345L;
+            e.zipkinDt = "22-07-07";
+            e.timestamp = new Date();
+            reports.add(e);
+        }
+        new Workbook("服务数据")
+            .setAutoSize(true)
+            .addSheet(new ListSheet<>("服务报表1", reports)).writeTo(Paths.get("d://tmp/"));
+    }
+
+    @Test public void testAutoSize2() throws IOException {
+        List<Map<String, ?>> reports = new ArrayList<>(2);
+        for (int i = 0; i < 2; i++) {
+            Map<String, Object> map = new LinkedHashMap<>();
+            for (int j = 1; j <= Const.Limit.MAX_COLUMNS_ON_SHEET; j++) {
+                map.put("COLUMN" + j, getRandomString());
+            }
+            reports.add(map);
+        }
+        new Workbook("服务数据")
+            .setAutoSize(true)
+            .addSheet(new ListMapSheet("服务报表1", reports).putExtProp(Const.ExtendPropertyKey.FREEZE, Panes.row(1))).writeTo(Paths.get("d://tmp/"));
+    }
+
+    @FreezePanes(topRow = 1)
+    public static class ServerReport {
+
+        @ExcelColumn(colIndex = 0, value = "序号")
+        private Integer index;
+
+        @ExcelColumn(colIndex = 2, value = "商户名")
+        private String name;
+
+        @ExcelColumn(colIndex = 1, value = "处理时间", hide = true, maxWidth = 254.88D)
+        private String zipkinDt;
+
+        @ExcelColumn(colIndex = 3, value = "时间", format = "yyyy-mm-dd hh:mm:ss")
+        private Date timestamp;
+
+        @ExcelColumn(colIndex = 4, value = "请求次数")
+        private Integer clientRequestNum;
+
+        @ExcelColumn(colIndex = 5, value = "持续时间")
+        private Long duration;
+    }
+}