Repository: wangguanquan/eec
Pull request #165 (2021-03-28 15:07:26)

Fix#86 修复导出时删除特殊字符的问题
- 修复导出时删除特殊字符的问题
- 增加wrapText属性控制单元格自动换行


Patch:
diff --git a/src/main/java/org/ttzero/excel/annotation/ExcelColumn.java b/src/main/java/org/ttzero/excel/annotation/ExcelColumn.java
index 8139e578..bdbcc3e0 100644
--- a/src/main/java/org/ttzero/excel/annotation/ExcelColumn.java
+++ b/src/main/java/org/ttzero/excel/annotation/ExcelColumn.java
@@ -75,4 +75,14 @@ public @interface ExcelColumn {
      * @return the data format string
      */
     String format() default "";
+
+    /**
+     * Wrap text in a cell
+     * <p>
+     * Microsoft Excel can wrap text so it appears on multiple lines in a cell.
+     * You can format the cell so the text wraps automatically, or enter a manual line break.
+     *
+     * @return true if
+     */
+    boolean wrapText() default false;
 }
diff --git a/src/main/java/org/ttzero/excel/entity/ListSheet.java b/src/main/java/org/ttzero/excel/entity/ListSheet.java
index 60527d64..9a99478c 100644
--- a/src/main/java/org/ttzero/excel/entity/ListSheet.java
+++ b/src/main/java/org/ttzero/excel/entity/ListSheet.java
@@ -531,6 +531,8 @@ public class ListSheet<T> extends Sheet {
             if (isNotEmpty(ec.format())) {
                 column.setNumFmt(ec.format());
             }
+            // Wrap
+            column.setWrapText(ec.wrapText());
             return column;
         }
         return null;
diff --git a/src/main/java/org/ttzero/excel/entity/Sheet.java b/src/main/java/org/ttzero/excel/entity/Sheet.java
index cb604e79..173ab451 100644
--- a/src/main/java/org/ttzero/excel/entity/Sheet.java
+++ b/src/main/java/org/ttzero/excel/entity/Sheet.java
@@ -266,6 +266,10 @@ public abstract class Sheet implements Cloneable, Storable {
          * Only export column name and ignore value
          */
         private boolean ignoreValue;
+        /**
+         * Wrap text in a cell
+         */
+        private int wrapText;
 
         /**
          * Constructor Column
@@ -757,7 +761,7 @@ public abstract class Sheet implements Cloneable, Storable {
         public int getCellStyle(Class<?> clazz) {
             int style;
             if (isString(clazz)) {
-                style = Styles.defaultStringBorderStyle();
+                style = Styles.defaultStringBorderStyle() | wrapText;
             } else if (isDate(clazz) || isLocalDate(clazz)) {
                 style = styles.addNumFmt(new NumFmt("yyyy\\-mm\\-dd")) | (1 << INDEX_BORDER) | Horizontals.CENTER;
             } else if (isDateTime(clazz) || isLocalDateTime(clazz)) {
@@ -830,6 +834,19 @@ public abstract class Sheet implements Cloneable, Storable {
             this.ignoreValue = true;
             return this;
         }
+
+        /**
+         * Wrap text in a cell
+         * <p>
+         * Microsoft Excel can wrap text so it appears on multiple lines in a cell.
+         * You can format the cell so the text wraps automatically, or enter a manual line break.
+         *
+         * @param wrapText set wrap
+         */
+        public Column setWrapText(boolean wrapText) {
+            this.wrapText = wrapText ? 1 : 0;
+            return this;
+        }
     }
 
     /**
diff --git a/src/main/java/org/ttzero/excel/entity/style/Styles.java b/src/main/java/org/ttzero/excel/entity/style/Styles.java
index 051e4e61..888dbb7a 100644
--- a/src/main/java/org/ttzero/excel/entity/style/Styles.java
+++ b/src/main/java/org/ttzero/excel/entity/style/Styles.java
@@ -59,8 +59,9 @@ import static org.ttzero.excel.util.StringUtil.isNotEmpty;
  *  8, 6 | Font
  * 14, 6 | Fill
  * 20, 6 | Border
- * 26, 3 | Vertical
- * 29, 3 | Horizontal</pre></blockquote>
+ * 26, 2 | Vertical
+ * 28, 3 | Horizontal
+ * 31. 1 | Warp Text</pre></blockquote>
  * The Build-In number format does not write into styles.
  *
  * @author guanquan.wang on 2017/10/13.
@@ -115,8 +116,9 @@ public class Styles implements Storable {
     public static final int INDEX_FONT = 18;
     public static final int INDEX_FILL = 12;
     public static final int INDEX_BORDER = 6;
-    public static final int INDEX_VERTICAL = 3;
-    public static final int INDEX_HORIZONTAL = 0;
+    public static final int INDEX_VERTICAL = 4;
+    public static final int INDEX_HORIZONTAL = 1;
+    public static final int INDEX_WRAP_TEXT = 0;
 
     /**
      * Create a general style
@@ -367,13 +369,14 @@ public class Styles implements Storable {
     }
 
     private static int[] unpack(int style) {
-        int[] styles = new int[6];
+        int[] styles = new int[7];
         styles[0] = style >>> INDEX_NUMBER_FORMAT;
         styles[1] = style << 8 >>> (INDEX_FONT + 8);
         styles[2] = style << 14 >>> (INDEX_FILL + 14);
         styles[3] = style << 20 >>> (INDEX_BORDER + 20);
         styles[4] = style << 26 >>> (INDEX_VERTICAL + 26);
-        styles[5] = style << 29 >>> (INDEX_HORIZONTAL + 29);
+        styles[5] = style << 28 >>> (INDEX_HORIZONTAL + 28);
+        styles[6] = style << 31 >>> (INDEX_WRAP_TEXT + 31);
         return styles;
     }
 
@@ -384,16 +387,18 @@ public class Styles implements Storable {
             | styles[3] << INDEX_BORDER
             | styles[4] << INDEX_VERTICAL
             | styles[5] << INDEX_HORIZONTAL
+            | styles[6] << INDEX_WRAP_TEXT
             ;
     }
 
-    static final String[] attrNames = {
+    private static final String[] attrNames = {
             "numFmtId"
             , "fontId"
             , "fillId"
             , "borderId"
             , "vertical"
             , "horizontal"
+            , "wrapText"
             , "applyNumberFormat"
             , "applyFont"
             , "applyFill"
@@ -427,7 +432,7 @@ public class Styles implements Storable {
             .addAttribute(attrNames[3], String.valueOf(styles[3]))
             .addAttribute("xfId", "0")
         ;
-        int start = 6;
+        int start = 7;
         if (styles[0] > 0) {
             newXf.addAttribute(attrNames[start], "1");
         }
@@ -440,7 +445,7 @@ public class Styles implements Storable {
         if (styles[3] > 0) {
             newXf.addAttribute(attrNames[start + 3], "1");
         }
-        if ((styles[4] | styles[5]) > 0) {
+        if ((styles[4] | styles[5] | styles[6]) > 0) {
             newXf.addAttribute(attrNames[start + 4], "1");
         }
 
@@ -448,6 +453,9 @@ public class Styles implements Storable {
         if (styles[5] > 0) {
             subEle.addAttribute(attrNames[5], Horizontals.of(styles[5]));
         }
+        if (styles[6] > 0) {
+            subEle.addAttribute(attrNames[6], "1");
+        }
         cellXfs.addAttribute("count", String.valueOf(count + 1));
         return n;
     }
@@ -501,7 +509,11 @@ public class Styles implements Storable {
     }
 
     public static int clearHorizontal(int style) {
-        return style & ~(-1 >>> 32 - (INDEX_VERTICAL - INDEX_HORIZONTAL));
+        return style & ~((-1 >>> 32 - (INDEX_VERTICAL - INDEX_HORIZONTAL)) << INDEX_HORIZONTAL);
+    }
+
+    public static int clearWrapText(int style) {
+        return style & ~(-1 >>> 32 - (INDEX_HORIZONTAL - INDEX_WRAP_TEXT));
     }
 
     ////////////////////////reset style/////////////////////////////
@@ -517,52 +529,52 @@ public class Styles implements Storable {
 
     ////////////////////////default border style/////////////////////////////
     public static int defaultCharBorderStyle() {
-        return (1 << INDEX_BORDER) | Horizontals.CENTER_CONTINUOUS;
+        return (1 << INDEX_BORDER) | (Horizontals.CENTER_CONTINUOUS << INDEX_HORIZONTAL);
     }
 
     public static int defaultStringBorderStyle() {
-        return (1 << INDEX_FONT) | (1 << INDEX_BORDER) | Horizontals.LEFT;
+        return (1 << INDEX_FONT) | (1 << INDEX_BORDER) | (Horizontals.LEFT << INDEX_HORIZONTAL);
     }
 
     public static int defaultIntBorderStyle() {
-        return (1 << INDEX_NUMBER_FORMAT) | (1 << INDEX_BORDER) | Horizontals.RIGHT;
+        return (1 << INDEX_NUMBER_FORMAT) | (1 << INDEX_BORDER) | (Horizontals.RIGHT << INDEX_HORIZONTAL);
     }
 
 //    public static int defaultDateBorderStyle() {
-//        return (176 << INDEX_NUMBER_FORMAT) | (1 << INDEX_BORDER) | Horizontals.CENTER;
+//        return (176 << INDEX_NUMBER_FORMAT) | (1 << INDEX_BORDER) | (Horizontals.CENTER << INDEX_HORIZONTAL);
 //    }
 //
 //    public static int defaultTimestampBorderStyle() {
-//        return (177 << INDEX_NUMBER_FORMAT) | (1 << INDEX_BORDER) | Horizontals.CENTER;
+//        return (177 << INDEX_NUMBER_FORMAT) | (1 << INDEX_BORDER) | (Horizontals.CENTER << INDEX_HORIZONTAL);
 //    }
 
     public static int defaultDoubleBorderStyle() {
-        return (2 << INDEX_NUMBER_FORMAT) | (1 << INDEX_FONT) | (1 << INDEX_BORDER) | Horizontals.RIGHT;
+        return (2 << INDEX_NUMBER_FORMAT) | (1 << INDEX_FONT) | (1 << INDEX_BORDER) | (Horizontals.RIGHT << INDEX_HORIZONTAL);
     }
 
     ////////////////////////default style/////////////////////////////
     public static int defaultCharStyle() {
-        return Horizontals.CENTER_CONTINUOUS;
+        return Horizontals.CENTER_CONTINUOUS << INDEX_HORIZONTAL;
     }
 
     public static int defaultStringStyle() {
-        return (1 << INDEX_FONT) | Horizontals.LEFT;
+        return (1 << INDEX_FONT) | (Horizontals.LEFT << INDEX_HORIZONTAL);
     }
 
     public static int defaultIntStyle() {
-        return (1 << INDEX_NUMBER_FORMAT) | Horizontals.RIGHT;
+        return (1 << INDEX_NUMBER_FORMAT) | (Horizontals.RIGHT << INDEX_HORIZONTAL);
     }
 
 //    public static int defaultDateStyle() {
-//        return (176 << INDEX_NUMBER_FORMAT) | Horizontals.CENTER;
+//        return (176 << INDEX_NUMBER_FORMAT) | (Horizontals.CENTER << INDEX_HORIZONTAL);
 //    }
 //
 //    public static int defaultTimestampStyle() {
-//        return (177 << INDEX_NUMBER_FORMAT) | Horizontals.CENTER;
+//        return (177 << INDEX_NUMBER_FORMAT) | (Horizontals.CENTER << INDEX_HORIZONTAL);
 //    }
 
     public static int defaultDoubleStyle() {
-        return (2 << INDEX_NUMBER_FORMAT) | (1 << INDEX_FONT) | Horizontals.RIGHT;
+        return (2 << INDEX_NUMBER_FORMAT) | (1 << INDEX_FONT) | (Horizontals.RIGHT << INDEX_HORIZONTAL);
     }
 
     ////////////////////////////////Check style////////////////////////////////
@@ -587,7 +599,11 @@ public class Styles implements Storable {
     }
 
     public static boolean hasHorizontal(int style) {
-        return style << 29 >>> (INDEX_HORIZONTAL + 29) != 0;
+        return style << 28 >>> (INDEX_HORIZONTAL + 28) != 0;
+    }
+
+    public static boolean hasWrapText(int style) {
+        return (style & 1) > 0;
     }
 
     ////////////////////////////////To object//////////////////////////////////
@@ -612,6 +628,14 @@ public class Styles implements Storable {
         return style << 26 >>> (INDEX_VERTICAL + 26);
     }
 
+    public int getHorizontal(int style) {
+        return style << 28 >>> (INDEX_HORIZONTAL + 28);
+    }
+
+    public int getWrapText(int style) {
+        return style & 1;
+    }
+
     /**
      * Returns the attribute value from Element
      *
diff --git a/src/main/java/org/ttzero/excel/util/ExtBufferedWriter.java b/src/main/java/org/ttzero/excel/util/ExtBufferedWriter.java
index bf78f6a5..4fd447bb 100644
--- a/src/main/java/org/ttzero/excel/util/ExtBufferedWriter.java
+++ b/src/main/java/org/ttzero/excel/util/ExtBufferedWriter.java
@@ -123,11 +123,11 @@ public class ExtBufferedWriter extends BufferedWriter {
             char c = block[i];
             if (c > 62) continue;
             // UnDisplay char
-            if (c < 32) {
-                write(block, last, i - last);
-                last = i + 1;
-                continue;
-            }
+//            if (c < 32) {
+//                write(block, last, i - last);
+//                last = i + 1;
+//                continue;
+//            }
             // html escape char
             char[] entity = ESCAPE_CHARS[c];
 


Test patch:
diff --git a/src/test/java/org/ttzero/excel/entity/ListMapSheetTest.java b/src/test/java/org/ttzero/excel/entity/ListMapSheetTest.java
index 3cf56140..f9c8e115 100644
--- a/src/test/java/org/ttzero/excel/entity/ListMapSheetTest.java
+++ b/src/test/java/org/ttzero/excel/entity/ListMapSheetTest.java
@@ -295,6 +295,15 @@ public class ListMapSheetTest extends WorkbookTest {
         }).writeTo(defaultTestPath);
     }
 
+    @Test public void testWrapText() throws IOException {
+        new Workbook("MAP WRAP TEXT", author)
+                .addSheet(createTestData(10)
+                    , new Sheet.Column("ID", "id", int.class)
+                    , new Sheet.Column("NAME", "name", String.class).setWrapText(true)
+                )
+                .writeTo(defaultTestPath);
+    }
+
     public static List<Map<String, ?>> createTestData() {
         int size = random.nextInt(100) + 1;
         return createTestData(size);
diff --git a/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest.java b/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest.java
index 6784aa7c..f0789eb1 100644
--- a/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest.java
+++ b/src/test/java/org/ttzero/excel/entity/ListObjectSheetTest.java
@@ -593,11 +593,19 @@ public class ListObjectSheetTest extends WorkbookTest {
             assert reader.sheet(1).dataRows().count() == 0L;
         }
     }
+
+    @Test public void testWrapText() throws IOException {
+        new Workbook("WRAP TEXT")
+                .addSheet(new ListSheet<>()
+                        .setData(Arrays.asList(new Item(1, "a b c\r\n1 2 3\r\n中文\t测试\r\nAAAAAA")
+                                , new Item(2, "fdsafdsafdsafdsafdsafdsafdsafdsfadsafdsafdsafdsafdsafdsafds"))))
+                .writeTo(defaultTestPath);
+    }
     
     public static class Item {
         @ExcelColumn
         private int id;
-        @ExcelColumn
+        @ExcelColumn(wrapText = true)
         private String name;
 
         public Item(int id, String name) {
diff --git a/src/test/java/org/ttzero/excel/entity/style/StylesTest.java b/src/test/java/org/ttzero/excel/entity/style/StylesTest.java
index 74c68086..9dcc8c30 100644
--- a/src/test/java/org/ttzero/excel/entity/style/StylesTest.java
+++ b/src/test/java/org/ttzero/excel/entity/style/StylesTest.java
@@ -24,7 +24,13 @@ import java.io.IOException;
 import java.nio.file.Path;
 
 import static org.ttzero.excel.entity.WorkbookTest.getOutputTestPath;
+import static org.ttzero.excel.entity.style.Styles.INDEX_BORDER;
+import static org.ttzero.excel.entity.style.Styles.INDEX_FILL;
+import static org.ttzero.excel.entity.style.Styles.INDEX_FONT;
+import static org.ttzero.excel.entity.style.Styles.INDEX_HORIZONTAL;
 import static org.ttzero.excel.entity.style.Styles.INDEX_NUMBER_FORMAT;
+import static org.ttzero.excel.entity.style.Styles.INDEX_VERTICAL;
+import static org.ttzero.excel.entity.style.Styles.INDEX_WRAP_TEXT;
 import static org.ttzero.excel.entity.style.Styles.testCodeIsDate;
 
 /**
@@ -120,4 +126,42 @@ public class StylesTest {
            else assert isDate;
         }
     }
+
+    @Test public void testClear() {
+        int style = (7 << INDEX_NUMBER_FORMAT) | (6 << INDEX_FONT)
+                | (5 << INDEX_FILL) | (4 << INDEX_BORDER)
+                | (3 << INDEX_VERTICAL) | (2 << INDEX_HORIZONTAL) | 1;
+
+        assert Styles.clearNumFmt(style) == style - (7 << INDEX_NUMBER_FORMAT);
+        assert Styles.clearFont(style) == style - (6 << INDEX_FONT);
+        assert Styles.clearFill(style) == style - (5 << INDEX_FILL);
+        assert Styles.clearBorder(style) == style - (4 << INDEX_BORDER);
+        assert Styles.clearVertical(style) == style - (3 << INDEX_VERTICAL);
+        assert Styles.clearHorizontal(style) == style - (2 << INDEX_HORIZONTAL);
+        assert Styles.clearWrapText(style) == style - (1 << INDEX_WRAP_TEXT);
+    }
+
+    @Test public void testHas() {
+        int style = (7 << INDEX_NUMBER_FORMAT) | (6 << INDEX_FONT)
+                | (5 << INDEX_FILL) | (4 << INDEX_BORDER)
+                | (3 << INDEX_VERTICAL) | (2 << INDEX_HORIZONTAL) | 1;
+
+        assert Styles.hasNumFmt(style);
+        assert Styles.hasFont(style);
+        assert Styles.hasFill(style);
+        assert Styles.hasBorder(style);
+        assert Styles.hasVertical(style);
+        assert Styles.hasHorizontal(style);
+        assert Styles.hasWrapText(style);
+
+
+        assert !Styles.hasNumFmt(Styles.clearNumFmt(style));
+        assert !Styles.hasFont(Styles.clearFont(style));
+        assert !Styles.hasFill(Styles.clearFill(style));
+        assert !Styles.hasBorder(Styles.clearBorder(style));
+        assert !Styles.hasVertical(Styles.clearVertical(style));
+        assert !Styles.hasHorizontal(Styles.clearHorizontal(style));
+        assert !Styles.hasWrapText(Styles.clearWrapText(style));
+    }
+
 }