diff --git a/website/.markdownlint-cli2.jsonc b/website/.markdownlint-cli2.jsonc index d85dcd35f..524637a0d 100644 --- a/website/.markdownlint-cli2.jsonc +++ b/website/.markdownlint-cli2.jsonc @@ -1,5 +1,7 @@ { - "globs": ["website/**/*.md"], + "globs": [ + "website/**/*.md" + ], "config": { "default": true, "MD001": true, @@ -7,17 +9,32 @@ "MD022": false, "MD041": false, "MD013": { - "line_length": 600, - "code_blocks": false, - "tables": false + "line_length": 600, + "code_blocks": false, + "tables": false }, "MD024": { - "siblings_only": true + "siblings_only": true }, "MD025": false, "MD029": true, "MD033": { - "allowed_elements": ["table", "tr", "td", "a", "img", "sub", "b", "br", "img", "tbody", "mark", "font"] + "allowed_elements": [ + "div", + "table", + "tr", + "td", + "a", + "img", + "sub", + "b", + "br", + "img", + "tbody", + "mark", + "font", + "p" + ] }, "MD036": false, "MD040": true, diff --git a/website/docs/sheet/fill/fill.md b/website/docs/sheet/fill/fill.md index c0f85bb66..d52e93b9f 100644 --- a/website/docs/sheet/fill/fill.md +++ b/website/docs/sheet/fill/fill.md @@ -24,6 +24,31 @@ title: 'Fill' This section explains how to use Fesod to fill data into files. +## Placeholder Syntax + +A template marks the cells to fill with `{}` placeholders. What sits inside the braces decides how +the cell is filled: + +| Placeholder | Meaning | Filled by | +| --- | --- | --- | +| `{name}` | a single variable | `doFill(object)`, `doFill(map)` | +| `{.name}` | the `name` property of every item of a list | `doFill(list)`, `fill(list, ...)` | +| `{data1.name}` | the same, for the list named `data1` | `fill(new FillWrapper("data1", list), ...)` | +| `\{name\}` | escaped with `\`, never parsed | nothing | + +The leading `.` is what makes a cell repeat once per item - downwards by default, or across the +columns with `FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL)`. The text before the +`.` names which list the items come from, so one template can hold several lists side by side. + +A cell may mix several placeholders with ordinary text, as in `{name} is {number} years old this +year`. A placeholder the fill does not supply is cleared rather than left in the sheet: filling a +list against `{name}`, or an object against `{.name}`, empties the cell and keeps only the text +around it. + +Escaping stops the braces from being parsed, but the `\` characters are only removed when the same +cell also holds a real placeholder. In a cell that contains nothing else, `\{name\}` is written out +exactly as typed, backslashes included. + ## Simple Fill ### Overview @@ -54,7 +79,7 @@ public void simpleFill() { // Approach 1: Fill based on object FillData fillData = new FillData(); - fillData.setName("张三"); + fillData.setName("John"); fillData.setNumber(5.2); FesodSheet.write("simpleFill.xlsx") .withTemplate(templateFileName) @@ -63,7 +88,7 @@ public void simpleFill() { // Approach 2: Fill based on Map Map map = new HashMap<>(); - map.put("name", "张三"); + map.put("name", "John"); map.put("number", 5.2); FesodSheet.write("simpleFillMap.xlsx") .withTemplate(templateFileName) @@ -74,11 +99,27 @@ public void simpleFill() { ### Template -![img](/img/docs/fill/simpleFill_file.png) +
+ + + + + + +
ABCDE
1NameNumberComplexIgnoredEmpty
2{name}{number}{name} is {number} years old\{name\} ignored,{name}Empty{.empty}
+
### Result -![img](/img/docs/fill/simpleFill_result.png) +
+ + + + + + +
ABCDE
1NameNumberComplexIgnoredEmpty
2John5.2John Doe is 5.2 years old{name} ignored,John DoeEmpty
+
--- @@ -113,11 +154,50 @@ public void listFill() { ### Template -![img](/img/docs/fill/listFill_file.png) +
+ + + + + + +
ABC
1NameNumberDate
2{.name}{.number}{.date}
+
### Result -![img](/img/docs/fill/listFill_result.png) +Approach 1: + +
+ + + + + + + + + + +
ABC
1NameNumberDate
2John Doe5.22026-07-31 19:55:44
3John Doe5.22026-07-31 19:55:44
4John Doe5.22026-07-31 19:55:44
11John Doe5.22026-07-31 19:55:44
+
+ +Approach 2: + +
+ + + + + + + + + + + +
ABC
1NameNumberDate
2John Doe5.22026-07-31 19:55:44
11John Doe5.22026-07-31 19:55:44
12John Doe5.22026-07-31 19:55:44
21John Doe5.22026-07-31 19:55:44
+
--- @@ -144,7 +224,7 @@ public void complexFill() { // Fill regular variables Map map = new HashMap<>(); - map.put("date", "2024年11月20日"); + map.put("date", "November 20, 2024"); map.put("total", 1000); writer.fill(map, writeSheet); } @@ -153,11 +233,37 @@ public void complexFill() { ### Template -![img](/img/docs/fill/complexFill_file.png) +
+ + + + + + + + + +
ABCD
1Statistics
2Time: {date}
3NameNumberNameNumber
4{.name}{.number}{.name}{.number}
5Total:{total}
+
### Result -![img](/img/docs/fill/complexFill_result.png) +
+ + + + + + + + + + + + + +
ABCD
1Statistics
2Time: November 20, 2024
3NameNumberNameNumber
4John Doe5.2John Doe5.2
5John Doe5.2John Doe5.2
6John Doe5.2John Doe5.2
13John Doe5.2John Doe5.2
14Total:1000
+
--- @@ -184,12 +290,12 @@ public void complexFillWithTable() { // Fill list data Map map = new HashMap<>(); - map.put("date", "2024年11月20日"); + map.put("date", "November 20, 2024"); writer.fill(map, writeSheet); // Fill statistical information List> totalList = new ArrayList<>(); - totalList.add(Arrays.asList(null, null, null, "统计: 1000")); + totalList.add(Arrays.asList(null, null, null, "Total: 1000")); writer.write(totalList, writeSheet); } } @@ -197,11 +303,40 @@ public void complexFillWithTable() { ### Template -![img](/img/docs/fill/complexFillWithTable_file.png) +
+ + + + + + + + +
ABCD
1Statistics
2Time: {date}
3NameNumberNameNumber
4{.name}{.number}{.name}{.number}
+
### Result -![img](/img/docs/fill/complexFillWithTable_result.png) +The file comes out the same as Complex Fill above. What changes is how it gets there. The template +stops at the list row instead of reserving a row for `{total}`, and the total is appended afterwards +with `writer.write(...)`, so the list can grow to any length without rows below it to push down. + +
+ + + + + + + + + + + + + +
ABCD
1Statistics
2Time: November 20, 2024
3NameNumberNameNumber
4John Doe5.2John Doe5.2
5John Doe5.2John Doe5.2
6John Doe5.2John Doe5.2
13John Doe5.2John Doe5.2
14Total: 1000
+
--- @@ -226,7 +361,7 @@ public void horizontalFill() { writer.fill(data(), config, writeSheet); Map map = new HashMap<>(); - map.put("date", "2024年11月20日"); + map.put("date", "November 20, 2024"); writer.fill(map, writeSheet); } } @@ -234,11 +369,33 @@ public void horizontalFill() { ### Template -![img](/img/docs/fill/horizontalFill_file.png) +
+ + + + + + + + + +
ABC
1StatisticsName{.name}
2Number{.number}
3Name{.name}
4Number{.number}
5Time: {date}
+
### Result -![img](/img/docs/fill/horizontalFill_result.png) +
+ + + + + + + + + +
ABCDEF
1StatisticsNameJohn DoeJohn DoeJohn DoeJohn Doe
2Number5.25.25.25.2
3NameJohn DoeJohn DoeJohn DoeJohn Doe
4Number5.25.25.25.2
5Time: November 20, 2024
+
--- @@ -260,7 +417,9 @@ public void compositeFill() { WriteSheet writeSheet = FesodSheet.writerSheet().build(); // Use FillWrapper for filling multiple lists - writer.fill(new FillWrapper("data1", data()), writeSheet); + // data1 is laid out across the columns, so it is filled horizontally + FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); + writer.fill(new FillWrapper("data1", data()), fillConfig, writeSheet); writer.fill(new FillWrapper("data2", data()), writeSheet); writer.fill(new FillWrapper("data3", data()), writeSheet); @@ -273,8 +432,51 @@ public void compositeFill() { ### Template -![img](/img/docs/fill/compositeFill_file.png) +
+ + + + + + + + + + + + + + + +
ABCDE
1StatisticsName{data1.name}
2Number{data1.number}
3Name{data1.name}
4Number{data1.number}
5Time: {date}
6
7
8NameNumber
9{data2.name}{data2.number}
10NameNumber
11{data3.name}{data3.number}
+
### Result -![img](/img/docs/fill/compositeFill_result.png) +- `data1` is filled horizontally, so its ten items run across the columns from `C` to `L` on each of the four template rows. +- `data2` and `data3` are filled downwards instead, occupying `A`/`B` in rows 9 to 18 and `D`/`E` in rows 11 to 20. +- Calling `fill` again with the same list name appends to it, as in [Fill List](#fill-list). + +
+ + + + + + + + + + + + + + + + + + + + +
ABCDEF
1StatisticsNameJohn DoeJohn DoeJohn DoeJohn Doe
2Number5.25.25.25.2
3NameJohn DoeJohn DoeJohn DoeJohn Doe
4Number5.25.25.25.2
5Time: 2026-07-31 20:04:59
6
7
8NameNumber
9John Doe5.2
10John Doe5.2NameNumber
11John Doe5.2John Doe5.2
12John Doe5.2John Doe5.2
18John Doe5.2John Doe5.2
19John Doe5.2
20John Doe5.2
+
diff --git a/website/docs/sheet/write/extra.md b/website/docs/sheet/write/extra.md index 0a4759d4c..dfc21a111 100644 --- a/website/docs/sheet/write/extra.md +++ b/website/docs/sheet/write/extra.md @@ -46,7 +46,7 @@ public class CommentWriteHandler implements RowWriteHandler { // Create comment in first row, second column Comment comment = drawingPatriarch.createCellComment( new XSSFClientAnchor(0, 0, 0, 0, (short) 1, 0, (short) 2, 1)); - comment.setString(new XSSFRichTextString("批注内容")); + comment.setString(new XSSFRichTextString("Comments")); sheet.getRow(0).getCell(1).setCellComment(comment); } } @@ -63,14 +63,27 @@ public void commentWrite() { FesodSheet.write(fileName, DemoData.class) .inMemory(Boolean.TRUE) // Comments must enable in-memory mode .registerWriteHandler(new CommentWriteHandler()) - .sheet("批注示例") + .sheet() .doWrite(data()); } ``` ### Result -![img](/img/docs/write/commentWrite.png) +The comment is attached to `B1` and is only shown when that cell is hovered. + +
+ + + + + + + + + +
ABC
1String TitleDate TitleCommentsNumber Title
2String02026-07-31 20:50:230.56
3String12026-07-31 20:50:230.56
11String92026-07-31 20:50:230.56
+
--- @@ -112,7 +125,15 @@ public void writeHyperlinkDataWrite() { ### Result -![img](/img/docs/write/writeCellDataWrite.png) +
+ + + + + + +
A
1hyperlink
2Click to visit
+
--- @@ -155,7 +176,15 @@ public void writeFormulaDataWrite() { ### Result -![img](/img/docs/write/writeCellDataWrite.png) +
+ + + + + + +
A
1formulaData
2=SUM(A1:A10)
+
--- @@ -182,45 +211,25 @@ public void templateWrite() { --- -## Merged Cells +## Custom Interceptors ### Overview -Supports merged cells through annotations or custom merge strategies. +Implement custom logic (such as adding dropdowns) through interceptor operations. ### Code Example -Annotation approach - -```java -@Getter -@Setter -@EqualsAndHashCode -public class DemoMergeData { - @ContentLoopMerge(eachRow = 2) // Merge every 2 rows - @ExcelProperty("字符串标题") - private String string; - - @ExcelProperty("日期标题") - private Date date; - - @ExcelProperty("数字标题") - private Double doubleData; -} -``` - -Custom merge strategy +Setting dropdowns ```java -public class CustomMergeStrategy extends AbstractMergeStrategy { +public class DropdownWriteHandler implements SheetWriteHandler { @Override - protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) { - // merge method will be called for each cell, ensuring that the same cell is merged only once - if (relativeRowIndex != null && relativeRowIndex % 2 == 0 && head.getColumnIndex() == 0) { - int startRow = relativeRowIndex + 1; // Row 0 is the header, data starts from row 1 - int endRow = startRow + 1; // Merge current row and next row - sheet.addMergedRegion(new CellRangeAddress(startRow, endRow, 0, 0)); - } + public void afterSheetCreate(SheetWriteHandlerContext context) { + DataValidationHelper helper = context.getWriteSheetHolder().getSheet().getDataValidationHelper(); + CellRangeAddressList range = new CellRangeAddressList(1, 10, 0, 0); // Dropdown area + DataValidationConstraint constraint = helper.createExplicitListConstraint(new String[] {"Option1", "Option2"}); + DataValidation validation = helper.createValidation(constraint, range); + context.getWriteSheetHolder().getSheet().addValidationData(validation); } } ``` @@ -229,51 +238,31 @@ Usage ```java @Test -public void mergeWrite() { - String fileName = "mergeWrite" + System.currentTimeMillis() + ".xlsx"; - - // Annotation approach - FesodSheet.write(fileName, DemoMergeData.class) - .sheet("合并示例") - .doWrite(data()); +public void dropdownWrite() { + String fileName = "dropdownWrite" + System.currentTimeMillis() + ".xlsx"; - // Custom merge strategy FesodSheet.write(fileName, DemoData.class) - .registerWriteHandler(new CustomMergeStrategy()) - .sheet("自定义合并") + .registerWriteHandler(new DropdownWriteHandler()) + .sheet("Dropdown Example") .doWrite(data()); } ``` ### Result -![img](/img/docs/write/mergeWrite.png) - ---- - -## Custom Interceptors - -### Overview - -Implement custom logic (such as adding dropdowns) through interceptor operations. - -### Code Example - -Setting dropdowns - -```java -public class DropdownWriteHandler implements SheetWriteHandler { - @Override - public void afterSheetCreate(SheetWriteHandlerContext context) { - DataValidationHelper helper = context.getWriteSheetHolder().getSheet().getDataValidationHelper(); - CellRangeAddressList range = new CellRangeAddressList(1, 10, 0, 0); // Dropdown area - DataValidationConstraint constraint = helper.createExplicitListConstraint(new String[] {"选项1", "选项2"}); - DataValidation validation = helper.createValidation(constraint, range); - context.getWriteSheetHolder().getSheet().addValidationData(validation); - } -} -``` - -### Result - -![img](/img/docs/write/customHandlerWrite.png) +The validation covers `A2:A11`, so every cell in that range offers the list. Selecting one shows the +dropdown button and its options - drawn open here on `A2`. + +
+ + + + + + + + + + +
ABC
1String TitleDate TitleNumber Title
2String0Option1Option22026-07-31 20:50:230.56
3String12026-07-31 20:50:230.56
4String22026-07-31 20:50:230.56
11String92026-07-31 20:50:230.56
+
diff --git a/website/docs/sheet/write/format.md b/website/docs/sheet/write/format.md index e0548b1a4..eb48f5a60 100644 --- a/website/docs/sheet/write/format.md +++ b/website/docs/sheet/write/format.md @@ -37,15 +37,15 @@ Supports date, number, or other custom formats through annotations. @Setter @EqualsAndHashCode public class ConverterData { - @ExcelProperty(value = "字符串标题", converter = CustomStringStringConverter.class) + @ExcelProperty(value = "String Title", converter = CustomStringStringConverter.class) private String string; - @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒") - @ExcelProperty("日期标题") + @DateTimeFormat("yyyy/MM/dd HH:mm:ss") + @ExcelProperty("Date Title") private Date date; @NumberFormat("#.##%") - @ExcelProperty("数字标题") + @ExcelProperty("Number Title") private Double doubleData; } ``` @@ -64,4 +64,16 @@ public void converterWrite() { ### Result -![img](/img/docs/write/converterWrite.png) +
+ + + + + + + + + + +
ABC
1String TitleDate TitleNumber Title
2Custom: String02024/12/03 20:50:2356.%
3Custom: String12024/12/03 20:50:2356.%
4Custom: String22024/12/03 20:50:2356.%
11Custom: String92024/12/03 20:50:2356.%
+
diff --git a/website/docs/sheet/write/head.md b/website/docs/sheet/write/head.md index 83a96ea57..37c70e8f1 100644 --- a/website/docs/sheet/write/head.md +++ b/website/docs/sheet/write/head.md @@ -38,11 +38,11 @@ Supports setting multi-level headers by specifying main titles and subtitles thr @Setter @EqualsAndHashCode public class ComplexHeadData { - @ExcelProperty({"主标题", "字符串标题"}) + @ExcelProperty({"Main Title", "String Title"}) private String string; - @ExcelProperty({"主标题", "日期标题"}) + @ExcelProperty({"Main Title", "Date Title"}) private Date date; - @ExcelProperty({"主标题", "数字标题"}) + @ExcelProperty({"Main Title", "Number Title"}) private Double doubleData; } ``` @@ -62,7 +62,20 @@ public void complexHeadWrite() { ### Result -![img](/img/docs/write/complexHeadWrite.png) +
+ + + + + + + + + + + +
ABC
1Main Title
2String TitleDate TitleNumber Title
3String02026-07-31 20:50:230.56
4String12026-07-31 20:50:230.56
5String22026-07-31 20:50:230.56
12String92026-07-31 20:50:230.56
+
--- @@ -81,9 +94,9 @@ public void dynamicHeadWrite() { String fileName = "dynamicHeadWrite" + System.currentTimeMillis() + ".xlsx"; List> head = Arrays.asList( - Collections.singletonList("动态字符串标题"), - Collections.singletonList("动态数字标题"), - Collections.singletonList("动态日期标题")); + Collections.singletonList("Dynamic String Title"), + Collections.singletonList("Dynamic Number Title"), + Collections.singletonList("Dynamic Date Title")); FesodSheet.write(fileName) .head(head) @@ -94,7 +107,19 @@ public void dynamicHeadWrite() { ### Result -![img](/img/docs/write/dynamicHeadWrite.png) +
+ + + + + + + + + + +
ABC
1Dynamic String TitleDynamic Number TitleDynamic Date Title
2String00.562026-07-31 20:50:23
3String10.562026-07-31 20:50:23
4String20.562026-07-31 20:50:23
11String90.562026-07-31 20:50:23
+
--- @@ -107,10 +132,14 @@ By default, Fesod automatically merges header cells with the same name. However, ### Merge Strategies - **NONE**: No automatic merging is performed. -- **HORIZONTAL_ONLY**: Only merges cells horizontally (same row). -- **VERTICAL_ONLY**: Only merges cells vertically (same column). -- **FULL_RECTANGLE**: Only merges complete rectangular regions where all cells have the same name. -- **AUTO**: Automatic merging (default). +- **HORIZONTAL_ONLY**: Only merges cells horizontally (adjacent columns in the same header row). +- **VERTICAL_ONLY**: Only merges cells vertically (adjacent rows in the same column). +- **FULL_RECTANGLE**: Merges both directions, but only where the repeated names form a complete rectangle. +- **AUTO**: Merges in both directions (default), except that a vertical merge starting below the top header + row also requires the cells directly above the two rows to match - see the note under the example. + +Strategies only matter for a **multi-level** header, since merging is driven by the same name repeating in adjacent +cells. The example below uses a three-level header where names repeat in both directions: ### Code Example @@ -120,9 +149,11 @@ public void dynamicHeadWriteWithStrategy() { String fileName = "dynamicHeadWrite" + System.currentTimeMillis() + ".xlsx"; List> head = Arrays.asList( - Collections.singletonList("动态字符串标题"), - Collections.singletonList("动态数字标题"), - Collections.singletonList("动态日期标题")); + Arrays.asList("Main Title", "ID", "ID"), + Arrays.asList("Main Title", "Group A", "Name"), + Arrays.asList("Main Title", "Group A", "Age"), + Arrays.asList("Main Title", "Group B", "Name"), + Arrays.asList("Main Title", "Group B", "Age")); FesodSheet.write(fileName) .head(head) @@ -132,6 +163,105 @@ public void dynamicHeadWriteWithStrategy() { } ``` +Each inner list is one **column**, listing its title from the top level down. So `"Main Title"` spans all five columns in row 1,\ +`"Group A"` covers columns B–C in row 2, and `"ID"` repeats down rows 2–3 of column A. + +#### NONE + +Every header cell stands alone: + +
+ + + + + + + +
ABCDE
1Main TitleMain TitleMain TitleMain TitleMain Title
2IDGroup AGroup AGroup BGroup B
3IDNameAgeNameAge
+
+ +#### HORIZONTAL_ONLY + +Merges `A1:E1`, `B2:C2`, `D2:E2`. The repeated `ID` in column A stays split: + +
+ + + + + + + +
ABCDE
1Main Title
2IDGroup AGroup B
3IDNameAgeNameAge
+
+ +#### VERTICAL_ONLY + +Merges only `A2:A3`; the row-spanning titles stay split: + +
+ + + + + + + +
ABCDE
1Main TitleMain TitleMain TitleMain TitleMain Title
2IDGroup AGroup AGroup BGroup B
3NameAgeNameAge
+
+ +#### FULL_RECTANGLE + +Merges `A1:E1`, `A2:A3`, `B2:C2`, `D2:E2`: + +
+ + + + + + + +
ABCDE
1Main Title
2IDGroup AGroup B
3NameAgeNameAge
+
+ +#### AUTO + +Merges `A1:E1`, `B2:C2`, `D2:E2`, but **not** `A2:A3`: + +
+ + + + + + + +
ABCDE
1Main Title
2IDGroup AGroup B
3IDNameAgeNameAge
+
+ +:::tip +`AUTO` merges horizontally exactly like `FULL_RECTANGLE`. The two differ only in **vertical** merging, where +`AUTO` looks at *where the repeated name starts*: + +- **It starts in header row 1.** The cells merge, with no extra condition, because there is no row above row 1 to + compare against. A column headed `ID` / `ID` / `ID` becomes one tall cell under `AUTO`, just as under + `FULL_RECTANGLE`. +- **It starts further down.** Two rows merge only if the cells **directly above them** hold the same name. + +The example above is the second case. `ID` repeats in rows 2 and 3, so `AUTO` compares what sits above each of them: +above row 2 is `Main Title`, above row 3 is `ID` itself. They differ, so the two cells are left separate. +`FULL_RECTANGLE` makes no such comparison and merges `A2:A3`. + +The condition is applied to each pair of rows in turn, so `AUTO` can merge just *part* of a repeated run. For a +column headed `Main Title` / `B` / `B` / `B`, `AUTO` merges rows 3–4 only (both sit under a `B`) and leaves row 2 +on its own, where `FULL_RECTANGLE` merges rows 2–4 into one cell. + +So if a column title that repeats down the levels should become one tall cell, choose `FULL_RECTANGLE` or +`VERTICAL_ONLY` explicitly. +::: + ### Common Use Cases **Disable merging**: Use `NONE` to completely disable automatic merging: @@ -144,4 +274,4 @@ FesodSheet.write(fileName) .doWrite(data()); ``` -**Note**: The old `automaticMergeHead` parameter is still supported for backward compatibility. When `headerMergeStrategy` is not set, the behavior is determined by `automaticMergeHead`. +**Note**: The old `automaticMergeHead` parameter is still supported for backward compatibility. When `headerMergeStrategy` is not set, the behavior is determined by `automaticMergeHead` (`true` → `AUTO`, `false` → `NONE`). diff --git a/website/docs/sheet/write/image.md b/website/docs/sheet/write/image.md index c8c3b37ae..21b2c5885 100644 --- a/website/docs/sheet/write/image.md +++ b/website/docs/sheet/write/image.md @@ -24,52 +24,205 @@ title: 'Image' This chapter introduces how to export files containing images. -## Image Export +## How Images Are Written -### Overview +An image is written as a floating picture anchored to its cell - the cell value itself stays empty. +The picture is stretched to the cell box, so its aspect ratio is not preserved: size the row and the +column to match with `@ContentRowHeight` and `@ColumnWidth`. -Supports exporting images through various methods including files, streams, byte arrays, URLs, etc. +## Image Sources -#### POJO Class +The declared field type selects the converter, so most sources need no configuration: -```java +| Field type | Converter | Notes | +| --- | --- | --- | +| `File` | `FileImageConverter` | A file on disk. | +| `InputStream` | `InputStreamImageConverter` | Read to the end; closing the stream stays your responsibility. | +| `byte[]`, `Byte[]` | `ByteArrayImageConverter`, `BoxingByteArrayImageConverter` | Raw image bytes. | +| `URL` | `UrlImageConverter` | Downloaded while the file is written, see [URL Sources](#url-sources). | +| `String` | none by default | Must be declared explicitly, see below. | + +`String` is the only source you have to declare, because an undeclared `String` field is written as +text. Choose the converter that matches the value: + +- `StringImageConverter` or `StringPathnameImageConverter` - a path to a file (the two behave identically). +- `StringBase64ImageConverter` - base64 data, with or without a `data:image/png;base64,` prefix. +## Image Export + +### POJO Class + +```java @Getter @Setter @EqualsAndHashCode @ContentRowHeight(100) @ColumnWidth(25) public class ImageDemoData { - private File file; - private InputStream inputStream; - @ExcelProperty(converter = StringImageConverter.class) - private String string; - private byte[] byteArray; - private URL url; + private File image; } ``` -#### Code Example +### Code Example ```java @Test -public void imageWrite() throws Exception { +public void imageWrite() { String fileName = "imageWrite" + System.currentTimeMillis() + ".xlsx"; String imagePath = "path/to/image.jpg"; - List list = new ArrayList<>(); ImageDemoData data = new ImageDemoData(); - data.setFile(new File(imagePath)); - data.setByteArray(Files.readAllBytes(Paths.get(imagePath))); - data.setUrl(new URL("https://example.com/image.jpg")); - list.add(data); + data.setImage(new File(imagePath)); FesodSheet.write(fileName, ImageDemoData.class) .sheet() - .doWrite(list); + .doWrite(Collections.singletonList(data)); } ``` ### Result -![img](/img/docs/write/imgWrite.png) +The column is named after the field, unless `@ExcelProperty` gives it a title. + +
+ + + + + + +
A
1image
2image
+
+ +Switching to another source is only a change of field type - the written picture is the same: + +```java +private InputStream image; // or byte[], Byte[], URL + +@ExcelProperty(converter = StringImageConverter.class) +private String image; // String needs the converter declared +``` + +## Multiple Images and Text in One Cell + +A `WriteCellData` field carries a list of `ImageData`, which lets one cell hold several images +alongside its text. Each image is placed with `top`/`right`/`bottom`/`left` margins in points, and +`relativeLastColumnIndex` lets an image extend into the columns to its right. + +### POJO Class + +```java +@Getter +@Setter +@EqualsAndHashCode +@ContentRowHeight(100) +@ColumnWidth(25) +public class ImageCellDemoData { + private WriteCellData image; +} +``` + +### Code Example + +```java +@Test +public void imageCellWrite() throws Exception { + String fileName = "imageCellWrite" + System.currentTimeMillis() + ".xlsx"; + byte[] imageBytes = Files.readAllBytes(Paths.get("path/to/image.jpg")); + + WriteCellData writeCellData = new WriteCellData<>(); + // Use CellDataTypeEnum.EMPTY if the cell needs no text of its own + writeCellData.setType(CellDataTypeEnum.STRING); + writeCellData.setStringValue("Additional text content"); + + List imageDataList = new ArrayList<>(); + writeCellData.setImageDataList(imageDataList); + + // First image: inset within the cell, kept clear of the right edge + ImageData imageData = new ImageData(); + imageDataList.add(imageData); + imageData.setImage(imageBytes); + imageData.setTop(5); + imageData.setRight(95); + imageData.setBottom(5); + imageData.setLeft(5); + + // Second image: starts further right and extends into the next column + imageData = new ImageData(); + imageDataList.add(imageData); + imageData.setImage(imageBytes); + imageData.setTop(5); + imageData.setRight(5); + imageData.setBottom(5); + imageData.setLeft(50); + // End one column to the right of this cell, so the image covers both + imageData.setRelativeLastColumnIndex(1); + + ImageCellDemoData data = new ImageCellDemoData(); + data.setImage(writeCellData); + + FesodSheet.write(fileName, ImageCellDemoData.class) + .sheet() + .doWrite(Collections.singletonList(data)); +} +``` + +The image format is detected from the data itself, so `ImageData.imageType` does not have to be set. +Margins larger than the cell can make Excel prompt to repair the file when it is opened. + +### Result + +Column `A` holds the text and both images; the second image overlaps column `B`. + +
+ + + + + + +
AB
1image
2imageimageAdditional text content
+
+ +## URL Sources + +A `URL` field is fetched over the network while the file is written, under a fetch polices: + +- Fefault allows `http` and `https` only; +- Refuses hosts resolving to a loopback, link-local, site-local or otherwise private address; +- Follows at most 3 redirects and reads at most 10 MB; +- The connect timeout is 1s and the read timeout 5s. + +A refused fetch fails the write with an `IOException` naming the rule that stopped it: + +```shell +URL image protocol is not allowed + +URL image host resolves to a restricted address + +URL image request exceeded redirect limit + +URL image data exceeds maximum size +``` + +The policy is global and can be replaced: + +```java +@Test +public void configureUrlImages() { + UrlImageConverter.setFetchPolicy(UrlImageFetchPolicy.builder() + .maxImageBytes(2 * 1024 * 1024) + .maxRedirects(1) + // allowPrivateNetwork on its own allows nothing - the host or its + // range has to be listed as well + .allowPrivateNetwork(true) + .allowedPrivateHosts(Collections.singletonList("images.internal")) + .allowedPrivateCidrs(Collections.singletonList(CidrBlock.parse("10.0.0.0/8"))) + .build()); + + UrlImageConverter.urlConnectTimeout = 2000; + UrlImageConverter.urlReadTimeout = 10000; +} +``` + +Call `UrlImageConverter.resetFetchPolicy()` to restore the defaults. diff --git a/website/docs/sheet/write/merge.md b/website/docs/sheet/write/merge.md index 4af637e47..99a495e13 100644 --- a/website/docs/sheet/write/merge.md +++ b/website/docs/sheet/write/merge.md @@ -67,14 +67,18 @@ public void annotationMergeWrite() { ### Result -```text -| String Title | Date Title | Number Title | -|--------------|---------------------|--------------| -| String0 | 2025-01-01 00:00:00 | 0.56 | -| (merged) | 2025-01-01 00:00:00 | 0.56 | -| String1 | 2025-01-01 00:00:00 | 0.56 | -| (merged) | 2025-01-01 00:00:00 | 0.56 | -``` +
+ + + + + + + + + +
ABC
1String TitleDate TitleNumber Title
2String02026-07-31 20:50:230.56
32026-07-31 20:50:230.56
4String12026-07-31 20:50:230.56
52026-07-31 20:50:230.56
+
--- diff --git a/website/docs/sheet/write/pojo.md b/website/docs/sheet/write/pojo.md index 0eb28f42c..f21e1236f 100644 --- a/website/docs/sheet/write/pojo.md +++ b/website/docs/sheet/write/pojo.md @@ -31,16 +31,21 @@ This chapter introduces how to write data by configuring POJO classes. Dynamically select columns to export by setting a collection of column names, supporting ignoring columns or exporting only specific columns. +The collection holds **POJO field names**, not header titles. Both examples below use the `DemoData` class and the +`data()` method from [Simple Writing](./simple.md), whose fields are `string`, `date` and `doubleData`. + ### Code Examples -Ignore specified columns +#### Ignore Specified Columns + +Everything except the listed fields is written: ```java @Test -public void excludeOrIncludeWrite() { +public void excludeColumnWrite() { String fileName = "excludeColumnFieldWrite" + System.currentTimeMillis() + ".xlsx"; - Set excludeColumns = Set.of("date"); + Set excludeColumns = Collections.singleton("date"); FesodSheet.write(fileName, DemoData.class) .excludeColumnFieldNames(excludeColumns) .sheet() @@ -48,24 +53,58 @@ public void excludeOrIncludeWrite() { } ``` -Export only specified columns +**Result** + +The `date` field is gone, the other two remain: + +
+ + + + + + + + + + +
AB
1String TitleNumber Title
2String00.56
3String10.56
4String20.56
11String90.56
+
+ +#### Export Only Specified Columns + +Only the listed fields are written: ```java @Test -public void excludeOrIncludeWrite() { - String fileName = "includeColumnFiledWrite" + System.currentTimeMillis() + ".xlsx"; +public void includeColumnWrite() { + String fileName = "includeColumnFieldWrite" + System.currentTimeMillis() + ".xlsx"; - Set includeColumns = Set.of("date"); + Set includeColumns = Collections.singleton("date"); FesodSheet.write(fileName, DemoData.class) - .includeColumnFiledNames(includeColumns) + .includeColumnFieldNames(includeColumns) .sheet() .doWrite(data()); } ``` -### Result +**Result** -![img](/img/docs/write/excludeOrIncludeWrite.png) +Only the `date` field is kept: + +
+ + + + + + + + + + +
A
1Date Title
22026-07-31 20:50:23
32026-07-31 20:50:23
42026-07-31 20:50:23
112026-07-31 20:50:23
+
--- @@ -73,7 +112,7 @@ public void excludeOrIncludeWrite() { ### Overview -Specify column order using the `index` attribute of the `@ExcelProperty` annotation. +Specify column order. ### POJO Class @@ -82,17 +121,21 @@ Specify column order using the `index` attribute of the `@ExcelProperty` annotat @Setter @EqualsAndHashCode public class IndexData { - @ExcelProperty(value = "字符串标题", index = 0) + @ExcelProperty(value = "String Title", index = 0) private String string; - @ExcelProperty(value = "日期标题", index = 1) + @ExcelProperty(value = "Date Title", index = 1) private Date date; - @ExcelProperty(value = "数字标题", index = 3) + @ExcelProperty(value = "Number Title", index = 3) private Double doubleData; } ``` ### Code Example +#### `index` attribute + +Using the `index` attribute of the `@ExcelProperty` annotation. + ```java @Test public void indexWrite() { @@ -103,9 +146,91 @@ public void indexWrite() { } ``` -### Result +**Result** + +
+ + + + + + + + + + +
ABCD
1String TitleDate TitleNumber Title
2String02026-07-31 20:50:230.56
3String12026-07-31 20:50:230.56
4String22026-07-31 20:50:230.56
11String92026-07-31 20:50:230.56
+
+ +:::note +Column **C is empty on purpose**. `index` is an absolute, 0-based column position, not a sort key: the fields declare +`0`, `1` and `3`, so nothing is written at position `2` and the gap is preserved in the output. To place the three +columns side by side, number them `0`, `1`, `2`. +::: + +#### includeColumnFieldNames + +The columns come out in the POJO's order, not the collection's. Listing `doubleData` before `string` still writes +`string` first, because that is the order the fields are declared in: + +```java +@Test +public void includeColumnOrderWrite() { + String fileName = "includeColumnFieldWrite" + System.currentTimeMillis() + ".xlsx"; + + Set includeColumns = new LinkedHashSet<>(Arrays.asList("doubleData", "string")); + FesodSheet.write(fileName, DemoData.class) + .includeColumnFieldNames(includeColumns) + .sheet() + .doWrite(data()); +} +``` + +**Result** + +
+ + + + + + + +
AB
1String TitleNumber Title
2String00.56
+
+ +#### orderByIncludeColumn + +Add `.orderByIncludeColumn(true)` to follow the collection's order instead: + +```java +@Test +public void orderByIncludeColumnWrite() { + String fileName = "includeColumnFieldWrite" + System.currentTimeMillis() + ".xlsx"; + + Set includeColumns = new LinkedHashSet<>(Arrays.asList("doubleData", "string")); + FesodSheet.write(fileName, DemoData.class) + .includeColumnFieldNames(includeColumns) + .orderByIncludeColumn(true) + .sheet() + .doWrite(data()); +} +``` + +**Result** + +
+ + + + + + + +
AB
1Number TitleString Title
20.56String0
+
-![img](/img/docs/write/indexWrite.png) +The collection needs a stable iteration order for this - a `LinkedHashSet` or a `List`, not a `HashSet`. --- @@ -124,21 +249,21 @@ public void noModelWrite() { FesodSheet.write(fileName) .head(head()) // Dynamic headers - .sheet("无对象写入") + .sheet("Write Without Object") .doWrite(dataList()); } private List> head() { return Arrays.asList( - Collections.singletonList("字符串标题"), - Collections.singletonList("数字标题"), - Collections.singletonList("日期标题")); + Collections.singletonList("String Title"), + Collections.singletonList("Number Title"), + Collections.singletonList("Date Title")); } private List> dataList() { List> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { - list.add(Arrays.asList("字符串" + i, 0.56, new Date())); + list.add(Arrays.asList("String" + i, 0.56, new Date())); } return list; } @@ -146,4 +271,16 @@ private List> dataList() { ### Result -![img](/img/docs/write/noModelWrite.png) +
+ + + + + + + + + + +
ABC
1String TitleNumber TitleDate Title
2String00.562026-07-31 20:50:23
3String10.562026-07-31 20:50:23
4String20.562026-07-31 20:50:23
11String90.562026-07-31 20:50:23
+
diff --git a/website/docs/sheet/write/sheet.md b/website/docs/sheet/write/sheet.md index 390efc8c4..a8087e8ab 100644 --- a/website/docs/sheet/write/sheet.md +++ b/website/docs/sheet/write/sheet.md @@ -51,7 +51,20 @@ public void writeSingleSheet() { ### Result -![img](/img/docs/write/repeatedWrite.png) +
+ + + + + + + + + + +
ABC
1String TitleDate TitleNumber Title
2String02026-07-31 20:50:230.56
3String12026-07-31 20:50:230.56
4String22026-07-31 20:50:230.56
51String92026-07-31 20:50:230.56
+
Sheet1
+
--- @@ -80,7 +93,20 @@ public void writeMultiSheet() { ### Result -![img](/img/docs/write/repeatedWrite.png) +
+ + + + + + + + + + +
ABC
1String TitleDate TitleNumber Title
2String02026-07-31 20:50:230.56
3String12026-07-31 20:50:230.56
4String22026-07-31 20:50:230.56
11String92026-07-31 20:50:230.56
+
Sheet0Sheet1Sheet2Sheet3Sheet4
+
--- @@ -98,10 +124,10 @@ Supports using multiple Tables within a single Sheet for segmented writing. public void tableWrite() { String fileName = "tableWrite" + System.currentTimeMillis() + ".xlsx"; - try (ExcelWriter excelWriter = FesodSheet.write(fileName, DemoData.class).build()) { - WriteSheet writeSheet = FesodSheet.writerSheet("Table示例").build(); - WriteTable table1 = FesodSheet.writerTable(0).build(); - WriteTable table2 = FesodSheet.writerTable(1).build(); + try (ExcelWriter excelWriter = FesodSheet.write(fileName).build()) { + WriteSheet writeSheet = FesodSheet.writerSheet("Table Example").build(); + WriteTable table1 = FesodSheet.writerTable(0).head(DemoData.class).build(); + WriteTable table2 = FesodSheet.writerTable(1).head(DemoData.class).build(); excelWriter.write(data(), writeSheet, table1); excelWriter.write(data(), writeSheet, table2); @@ -111,4 +137,32 @@ public void tableWrite() { ### Result -![img](/img/docs/write/tableWrite.png) +
+ + + + + + + + + + + + + + + + +
ABC
1String TitleDate TitleNumber Title
2String02026-07-31 20:50:230.56
3String12026-07-31 20:50:230.56
4String22026-07-31 20:50:230.56
11String92026-07-31 20:50:230.56
12String TitleDate TitleNumber Title
13String02026-07-31 20:50:230.56
14String12026-07-31 20:50:230.56
15String22026-07-31 20:50:230.56
22String92026-07-31 20:50:230.56
+
Table Example
+
+ +:::note +Each `WriteTable` writes its own header, so the header repeats once per table - rows 1 and 12 - with the two batches +of 10 rows at 2-11 and 13-22. + +The head is declared on each `WriteTable` rather than on the writer, which is also what lets the tables carry +different data types. Building the writer with `FesodSheet.write(fileName, DemoData.class)` instead gives the sheet a +head of its own, and it writes an extra header row above the first table's. +::: diff --git a/website/docs/sheet/write/simple.md b/website/docs/sheet/write/simple.md index 504ab304b..1cfb6e27d 100644 --- a/website/docs/sheet/write/simple.md +++ b/website/docs/sheet/write/simple.md @@ -41,11 +41,11 @@ The `DemoData` POJO class corresponding to the spreadsheet structure: @Setter @EqualsAndHashCode public class DemoData { - @ExcelProperty("字符串标题") + @ExcelProperty("String Title") private String string; - @ExcelProperty("日期标题") + @ExcelProperty("Date Title") private Date date; - @ExcelProperty("数字标题") + @ExcelProperty("Number Title") private Double doubleData; @ExcelIgnore private String ignore; // Ignored field @@ -114,3 +114,22 @@ public void simpleWrite() { } } ``` + +### Result + +All three approaches produce the same file. Row 1 holds the header titles taken from `@ExcelProperty`, and the 10 +objects returned by `data()` follow in rows 2-11. The `ignore` field is absent because it is annotated `@ExcelIgnore`. + +
+ + + + + + + + + + +
ABC
1String TitleDate TitleNumber Title
2String02026-07-31 20:50:230.56
3String12026-07-31 20:50:230.56
4String22026-07-31 20:50:230.56
11String92026-07-31 20:50:230.56
+
diff --git a/website/docs/sheet/write/style.md b/website/docs/sheet/write/style.md index 3b4a6de7d..821e24e13 100644 --- a/website/docs/sheet/write/style.md +++ b/website/docs/sheet/write/style.md @@ -50,13 +50,13 @@ public class DemoStyleData { @HeadFontStyle(fontHeightInPoints = 30) @ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 40) @ContentFontStyle(fontHeightInPoints = 30) - @ExcelProperty("字符串标题") + @ExcelProperty("String Title") private String string; - @ExcelProperty("日期标题") + @ExcelProperty("Date Title") private Date date; - @ExcelProperty("数字标题") + @ExcelProperty("Number Title") private Double doubleData; } ``` @@ -77,7 +77,19 @@ public void annotationStyleWrite() { ### Result -![img](/img/docs/write/annotationStyleWrite.png) +
+ + + + + + + + + + +
ABC
1String TitleDate TitleNumber Title
2String02026-07-31 20:50:230.56
3String12026-07-31 20:50:230.56
4String22026-07-31 20:50:230.56
11String92026-07-31 20:50:230.56
+
--- @@ -115,14 +127,26 @@ public void handlerStyleWrite() { FesodSheet.write(fileName, DemoData.class) .registerWriteHandler(styleStrategy) - .sheet("样式模板") + .sheet("Style Template") .doWrite(data()); } ``` ### Result -![img](/img/docs/write/handlerStyleWrite.png) +
+ + + + + + + + + + +
ABC
1String TitleDate TitleNumber Title
2String02026-07-31 20:50:230.56
3String12026-07-31 20:50:230.56
4String22026-07-31 20:50:230.56
11String92026-07-31 20:50:230.56
+
--- @@ -158,7 +182,7 @@ public class CustomCellStyleWriteHandler implements CellWriteHandler { writeFont.setFontHeightInPoints((short) 14); // Font size 14 writeCellStyle.setWriteFont(writeFont); - log.info("已自定义单元格样式: 行 {}, 列 {}", context.getRowIndex(), context.getColumnIndex()); + log.info("Custom cell style applied: row {}, column {}", context.getRowIndex(), context.getColumnIndex()); } } } @@ -173,7 +197,7 @@ public void customCellStyleWrite() { FesodSheet.write(fileName, DemoData.class) .registerWriteHandler(new CustomCellStyleWriteHandler()) - .sheet("自定义样式") + .sheet("Custom Style") .doWrite(data()); } ``` @@ -209,7 +233,7 @@ public void poiStyleWrite() { } } }) - .sheet("POI样式") + .sheet("POI Style") .doWrite(data()); } ``` @@ -232,14 +256,14 @@ Control column width and row height through annotations, suitable for scenarios @HeadRowHeight(30) @ColumnWidth(25) // Default column width public class WidthAndHeightData { - @ExcelProperty("字符串标题") + @ExcelProperty("String Title") private String string; - @ExcelProperty("日期标题") + @ExcelProperty("Date Title") private Date date; @ColumnWidth(50) // Individually set column width - @ExcelProperty("数字标题") + @ExcelProperty("Number Title") private Double doubleData; } ``` @@ -259,4 +283,16 @@ public void widthAndHeightWrite() { ### Result -![img](/img/docs/write/widthAndHeightWrite.png) +
+ + + + + + + + + + +
ABC
1String TitleDate TitleNumber Title
2String02026-07-31 20:50:230.56
3String12026-07-31 20:50:230.56
4String22026-07-31 20:50:230.56
11String92026-07-31 20:50:230.56
+
diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 8b4d928e0..02a3b3496 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -69,7 +69,7 @@ const config = { authorsMapPath: "authors.json" }, theme: { - customCss: './src/css/custom.css' + customCss: ['./src/css/custom.css', './src/css/xl-sheet.css'] }, } ], diff --git a/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/fill/fill.md b/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/fill/fill.md index 950d7c9f0..7976e001d 100644 --- a/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/fill/fill.md +++ b/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/fill/fill.md @@ -7,6 +7,23 @@ title: '填充' 本章节介绍如何使用 Fesod 来填充数据到文件中。 +## 占位符语法 + +模板通过 `{}` 占位符标记需要填充的单元格,大括号里的内容决定了填充方式: + +| 占位符 | 含义 | 由谁填充 | +| --- | --- | --- | +| `{name}` | 单个变量 | `doFill(object)`、`doFill(map)` | +| `{.name}` | 列表中每一项的 `name` 属性 | `doFill(list)`、`fill(list, ...)` | +| `{data1.name}` | 同上,但取自名为 `data1` 的列表 | `fill(new FillWrapper("data1", list), ...)` | +| `\{name\}` | 用 `\` 转义,不会被解析 | 不填充 | + +开头的 `.` 决定了单元格会按列表的每一项重复:默认向下重复,也可以通过 `FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL)` 向右重复。`.` 之前的文字指明数据来自哪个列表,因此一个模板中可以并存多个列表。 + +一个单元格里可以混合多个占位符和普通文本,例如 `{name}今年{number}岁了`。填充时没有提供的占位符会被清空,而不是原样留在表格里:用列表填充 `{name}`,或者用单个对象填充 `{.name}`,都会清空该单元格,只保留周围的文本。 + +转义只是让大括号不被解析,而 `\` 本身只有在同一个单元格里还存在真实占位符时才会被去掉。如果单元格里没有别的内容,`\{name\}` 会原样写出,包括反斜杠。 + ## 简单填充 ### 概述 @@ -57,11 +74,27 @@ public void simpleFill() { ### 模板 -![img](/img/docs/fill/simpleFill_file.png) +
+ + + + + + +
ABCDE
1姓名数字复杂忽略
2{name}{number}{name}今年{number}岁了\{name\}忽略,{name}空{.empty}
+
### 结果 -![img](/img/docs/fill/simpleFill_result.png) +
+ + + + + + +
ABCDE
1姓名数字复杂忽略
2张三5.2张三今年5.2岁了{name}忽略,张三
+
--- @@ -96,11 +129,50 @@ public void listFill() { ### 模板 -![img](/img/docs/fill/listFill_file.png) +
+ + + + + + +
ABC
1姓名数字日期
2{.name}{.number}{.date}
+
### 结果 -![img](/img/docs/fill/listFill_result.png) +方案1: + +
+ + + + + + + + + + +
ABC
1姓名数字日期
2张三5.22026-07-31 19:55:44
3张三5.22026-07-31 19:55:44
4张三5.22026-07-31 19:55:44
11张三5.22026-07-31 19:55:44
+
+ +方案2: + +
+ + + + + + + + + + + +
ABC
1姓名数字日期
2张三5.22026-07-31 19:55:44
11张三5.22026-07-31 19:55:44
12张三5.22026-07-31 19:55:44
21张三5.22026-07-31 19:55:44
+
--- @@ -136,11 +208,37 @@ public void complexFill() { ### 模板 -![img](/img/docs/fill/complexFill_file.png) +
+ + + + + + + + + +
ABCD
1统计
2时间:{date}
3姓名数字姓名数字
4{.name}{.number}{.name}{.number}
5统计:{total}
+
### 结果 -![img](/img/docs/fill/complexFill_result.png) +
+ + + + + + + + + + + + + +
ABCD
1统计
2时间:2024年11月20日
3姓名数字姓名数字
4张三5.2张三5.2
5张三5.2张三5.2
6张三5.2张三5.2
13张三5.2张三5.2
14统计:1000
+
--- @@ -179,11 +277,38 @@ public void complexFillWithTable() { ### 模板 -![img](/img/docs/fill/complexFillWithTable_file.png) +
+ + + + + + + + +
ABCD
1统计
2时间:{date}
3姓名数字姓名数字
4{.name}{.number}{.name}{.number}
+
### 结果 -![img](/img/docs/fill/complexFillWithTable_result.png) +最终文件与上面的复杂填充相同,区别在于生成方式:模板到列表行为止,不再为 `{total}` 预留一行,统计信息在填充之后通过 `writer.write(...)` 追加,因此列表可以任意增长,下方没有需要被顶下去的行。 + +
+ + + + + + + + + + + + + +
ABCD
1统计
2时间:2024年11月20日
3姓名数字姓名数字
4张三5.2张三5.2
5张三5.2张三5.2
6张三5.2张三5.2
13张三5.2张三5.2
14统计: 1000
+
--- @@ -216,11 +341,33 @@ public void horizontalFill() { ### 模板 -![img](/img/docs/fill/horizontalFill_file.png) +
+ + + + + + + + + +
ABC
1统计姓名{.name}
2数字{.number}
3姓名{.name}
4数字{.number}
5时间:{date}
+
### 结果 -![img](/img/docs/fill/horizontalFill_result.png) +
+ + + + + + + + + +
ABCDEF
1统计姓名张三张三张三张三
2数字5.25.25.25.2
3姓名张三张三张三张三
4数字5.25.25.25.2
5时间:2024年11月20日
+
--- @@ -242,7 +389,9 @@ public void compositeFill() { WriteSheet writeSheet = FesodSheet.writerSheet().build(); // 使用 FillWrapper 进行多列表填充 - writer.fill(new FillWrapper("data1", data()), writeSheet); + // data1 在模板中是横向排列的,因此使用横向填充 + FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build(); + writer.fill(new FillWrapper("data1", data()), fillConfig, writeSheet); writer.fill(new FillWrapper("data2", data()), writeSheet); writer.fill(new FillWrapper("data3", data()), writeSheet); @@ -255,8 +404,51 @@ public void compositeFill() { ### 模板 -![img](/img/docs/fill/compositeFill_file.png) +
+ + + + + + + + + + + + + + + +
ABCDE
1统计姓名{data1.name}
2数字{data1.number}
3姓名{data1.name}
4数字{data1.number}
5时间:{date}
6
7
8姓名数字
9{data2.name}{data2.number}
10姓名数字
11{data3.name}{data3.number}
+
### 结果 -![img](/img/docs/fill/compositeFill_result.png) +- `data1` 采用横向填充,十条数据在四个模板行上从 `C` 列排到 `L` 列。 +- `data2` 和 `data3` 则是纵向填充,分别占据第 9 到 18 行的 `A`/`B` 列和第 11 到 20 行的 `D`/`E` 列。 +- 用同一个列表名调用 `fill` 会接着往后追加,参见[填充列表](#填充列表)。 + +
+ + + + + + + + + + + + + + + + + + + + +
ABCDEF
1统计姓名张三张三张三张三
2数字5.25.25.25.2
3姓名张三张三张三张三
4数字5.25.25.25.2
5时间:2026-07-31 20:04:59
6
7
8姓名数字
9张三5.2
10张三5.2姓名数字
11张三5.2张三5.2
12张三5.2张三5.2
18张三5.2张三5.2
19张三5.2
20张三5.2
+
diff --git a/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/extra.md b/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/extra.md index feca7114a..9426c239f 100644 --- a/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/extra.md +++ b/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/extra.md @@ -29,7 +29,7 @@ public class CommentWriteHandler implements RowWriteHandler { // 在第一行第二列创建批注 Comment comment = drawingPatriarch.createCellComment( new XSSFClientAnchor(0, 0, 0, 0, (short) 1, 0, (short) 2, 1)); - comment.setString(new XSSFRichTextString("批注内容")); + comment.setString(new XSSFRichTextString("批注1")); sheet.getRow(0).getCell(1).setCellComment(comment); } } @@ -46,14 +46,27 @@ public void commentWrite() { FesodSheet.write(fileName, DemoData.class) .inMemory(Boolean.TRUE) // 批注必须启用内存模式 .registerWriteHandler(new CommentWriteHandler()) - .sheet("批注示例") + .sheet() .doWrite(data()); } ``` ### 结果 -![img](/img/docs/write/commentWrite.png) +在第一行第二列单元格上出现批注信息。 + +
+ + + + + + + + + +
ABC
1字符串标题日期标题批注1数字标题
2字符串02026-07-31 20:50:230.56
3字符串12026-07-31 20:50:230.56
11字符串92026-07-31 20:50:230.56
+
--- @@ -95,7 +108,15 @@ public void writeHyperlinkDataWrite() { ### 结果 -![img](/img/docs/write/writeCellDataWrite.png) +
+ + + + + + +
A
1hyperlink
2点击访问
+
--- @@ -139,7 +160,15 @@ public void writeFormulaDataWrite() { ### 结果 -![img](/img/docs/write/writeCellDataWrite.png) +
+ + + + + + +
A
1formulaData
2=SUM(A1:A10)
+
--- @@ -166,75 +195,6 @@ public void templateWrite() { --- -## 合并单元格 - -### 概述 - -支持通过注解或自定义合并策略实现合并单元格。 - -### 代码示例 - -注解方式 - -```java -@Getter -@Setter -@EqualsAndHashCode -public class DemoMergeData { - @ContentLoopMerge(eachRow = 2) // 每隔 2 行合并一次 - @ExcelProperty("字符串标题") - private String string; - - @ExcelProperty("日期标题") - private Date date; - - @ExcelProperty("数字标题") - private Double doubleData; -} -``` - -自定义合并策略 - -```java -public class CustomMergeStrategy extends AbstractMergeStrategy { - @Override - protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) { - // merge方法会为每个单元格都调用一次,确保相同单元格只执行一次合并 - if (relativeRowIndex != null && relativeRowIndex % 2 == 0 && head.getColumnIndex() == 0) { - int startRow = relativeRowIndex + 1; // 第0行是表头,数据从第1行开始 - int endRow = startRow + 1; // 合并当前行和下一行 - sheet.addMergedRegion(new CellRangeAddress(startRow, endRow, 0, 0)); - } - } -} -``` - -使用 - -```java -@Test -public void mergeWrite() { - String fileName = "mergeWrite" + System.currentTimeMillis() + ".xlsx"; - - // 注解方式 - FesodSheet.write(fileName, DemoMergeData.class) - .sheet("合并示例") - .doWrite(data()); - - // 自定义合并策略 - FesodSheet.write(fileName, DemoData.class) - .registerWriteHandler(new CustomMergeStrategy()) - .sheet("自定义合并") - .doWrite(data()); -} -``` - -### 结果 - -![img](/img/docs/write/mergeWrite.png) - ---- - ## 自定义拦截器 ### 概述 @@ -258,6 +218,34 @@ public class DropdownWriteHandler implements SheetWriteHandler { } ``` +使用 + +```java +@Test +public void dropdownWrite() { + String fileName = "dropdownWrite" + System.currentTimeMillis() + ".xlsx"; + + FesodSheet.write(fileName, DemoData.class) + .registerWriteHandler(new DropdownWriteHandler()) + .sheet("下拉框示例") + .doWrite(data()); +} +``` + ### 结果 -![img](/img/docs/write/customHandlerWrite.png) +校验范围是 `A2:A11`,该区域内的每个单元格都可以选择下拉框中的值。 + +
+ + + + + + + + + + +
ABC
1字符串标题日期标题数字标题
2字符串0选项1选项22026-07-31 20:50:230.56
3字符串12026-07-31 20:50:230.56
4字符串22026-07-31 20:50:230.56
11字符串92026-07-31 20:50:230.56
+
diff --git a/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/format.md b/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/format.md index 2f27f7f33..23bf4135b 100644 --- a/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/format.md +++ b/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/format.md @@ -47,4 +47,16 @@ public void converterWrite() { ### 结果 -![img](/img/docs/write/converterWrite.png) +
+ + + + + + + + + + +
ABC
1字符串标题日期标题数字标题
2自定义:字符串02024年12月03日20时50分23秒56.%
3自定义:字符串12024年12月03日20时50分23秒56.%
4自定义:字符串22024年12月03日20时50分23秒56.%
11自定义:字符串92024年12月03日20时50分23秒56.%
+
diff --git a/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/head.md b/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/head.md index db7620d6f..c6851d887 100644 --- a/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/head.md +++ b/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/head.md @@ -45,7 +45,20 @@ public void complexHeadWrite() { ### 结果 -![img](/img/docs/write/complexHeadWrite.png) +
+ + + + + + + + + + + +
ABC
1主标题
2字符串标题日期标题数字标题
3字符串02026-07-31 20:50:230.56
4字符串12026-07-31 20:50:230.56
5字符串22026-07-31 20:50:230.56
12字符串92026-07-31 20:50:230.56
+
--- @@ -77,7 +90,19 @@ public void dynamicHeadWrite() { ### 结果 -![img](/img/docs/write/dynamicHeadWrite.png) +
+ + + + + + + + + + +
ABC
1动态字符串标题动态数字标题动态日期标题
2字符串00.562026-07-31 20:50:23
3字符串10.562026-07-31 20:50:23
4字符串20.562026-07-31 20:50:23
11字符串90.562026-07-31 20:50:23
+
--- @@ -90,10 +115,13 @@ public void dynamicHeadWrite() { ### 合并策略 - **NONE**: 不进行任何自动合并。 -- **HORIZONTAL_ONLY**: 仅水平合并(同一行内的相同单元格)。 -- **VERTICAL_ONLY**: 仅垂直合并(同一列内的相同单元格)。 -- **FULL_RECTANGLE**: 仅合并完整的矩形区域(所有单元格名称相同)。 -- **AUTO**: 自动合并(默认)。 +- **HORIZONTAL_ONLY**: 仅水平合并(同一表头行内相邻的列)。 +- **VERTICAL_ONLY**: 仅垂直合并(同一列内相邻的行)。 +- **FULL_RECTANGLE**: 双向合并,但仅在重复的名称构成完整矩形区域时才合并。 +- **AUTO**: 双向合并(默认),但当垂直合并从非首行开始时,还要求两行**正上方**的单元格内容相同 —— 详见示例下方的说明。 + +合并是由相邻单元格中重复出现的相同名称驱动的,因此只有**多级**表头才需要关心合并策略。下面的示例使用一个三级表头, +名称在水平和垂直两个方向上都有重复: ### 代码示例 @@ -103,9 +131,11 @@ public void dynamicHeadWriteWithStrategy() { String fileName = "dynamicHeadWrite" + System.currentTimeMillis() + ".xlsx"; List> head = Arrays.asList( - Collections.singletonList("动态字符串标题"), - Collections.singletonList("动态数字标题"), - Collections.singletonList("动态日期标题")); + Arrays.asList("主标题", "编号", "编号"), + Arrays.asList("主标题", "A 组", "姓名"), + Arrays.asList("主标题", "A 组", "年龄"), + Arrays.asList("主标题", "B 组", "姓名"), + Arrays.asList("主标题", "B 组", "年龄")); FesodSheet.write(fileName) .head(head) @@ -115,6 +145,97 @@ public void dynamicHeadWriteWithStrategy() { } ``` +#### NONE + +每个表头单元格都各自独立: + +
+ + + + + + + +
ABCDE
1主标题主标题主标题主标题主标题
2编号A 组A 组B 组B 组
3编号姓名年龄姓名年龄
+
+ +#### HORIZONTAL_ONLY + +合并 `A1:E1`、`B2:C2`、`D2:E2` + +
+ + + + + + + +
ABCDE
1主标题
2编号A 组B 组
3编号姓名年龄姓名年龄
+
+ +#### VERTICAL_ONLY + +只合并 `A2:A3` + +
+ + + + + + + +
ABCDE
1主标题主标题主标题主标题主标题
2编号A 组A 组B 组B 组
3姓名年龄姓名年龄
+
+ +#### FULL_RECTANGLE + +合并 `A1:E1`、`A2:A3`、`B2:C2`、`D2:E2` + +
+ + + + + + + +
ABCDE
1主标题
2编号A 组B 组
3姓名年龄姓名年龄
+
+ +#### AUTO + +合并 `A1:E1`、`B2:C2`、`D2:E2`,但不会合并 `A2:A3`: + +
+ + + + + + + +
ABCDE
1主标题
2编号A 组B 组
3编号姓名年龄姓名年龄
+
+ +:::tip +`AUTO` 的水平合并与 `FULL_RECTANGLE` 完全一致,两者的区别只在**垂直**合并上。`AUTO` 会看*重复的名称从哪一行开始*: + +- **从表头第 1 行开始**:直接合并,没有额外条件,因为第 1 行上方没有可供比较的行。某列表头为 + `编号` / `编号` / `编号` 时,在 `AUTO` 下同样会合并成一个纵向单元格,与 `FULL_RECTANGLE` 一致。 +- **从更下面的行开始**:只有当两行**正上方**的单元格内容相同时才会合并。 + +上面的示例属于第二种情况。`编号` 在第 2、3 行重复,`AUTO` 于是比较它们各自上方的内容:第 2 行上方是 `主标题`, +第 3 行上方却是 `编号` 自身,两者不同,因此这两个单元格保持独立。`FULL_RECTANGLE` 不做这个比较,会合并 `A2:A3`。 + +该条件是逐对行判断的,所以 `AUTO` 可能只合并重复区段的**一部分**。例如某列表头为 `主标题` / `B` / `B` / `B` 时, +`AUTO` 只会合并第 3–4 行(两者上方都是 `B`),第 2 行单独保留;而 `FULL_RECTANGLE` 会把第 2–4 行合并为一个单元格。 + +因此,如果希望某个在各级中重复出现的列标题合并成一个纵向单元格,请显式选择 `FULL_RECTANGLE` 或 `VERTICAL_ONLY`。 +::: + ### 常见使用场景 **禁用合并**: 使用 `NONE` 完全禁用自动合并: @@ -127,4 +248,4 @@ FesodSheet.write(fileName) .doWrite(data()); ``` -**注意**: 旧的 `automaticMergeHead` 参数仍然支持以保持向后兼容。当未设置 `headerMergeStrategy` 时,行为由 `automaticMergeHead` 决定。 +**注意**: 旧的 `automaticMergeHead` 参数仍然支持以保持向后兼容。当未设置 `headerMergeStrategy` 时,行为由 `automaticMergeHead` 决定(`true` → `AUTO`,`false` → `NONE`)。 diff --git a/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/image.md b/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/image.md index 028138032..37570e2c2 100644 --- a/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/image.md +++ b/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/image.md @@ -7,13 +7,30 @@ title: '图片' 本章节介绍如何导出包含图片的文件。 -## 图片导出 +## 图片的写出方式 + +图片以浮动图形的形式写出,锚定在单元格上,单元格本身的值仍然为空。图片会被拉伸到单元格大小,不保持原始比例,因此需要用 `@ContentRowHeight` 和 `@ColumnWidth` 把行高列宽调整到合适尺寸。 + +## 图片来源 + +字段声明的类型决定使用哪个转换器,因此大部分来源无需额外配置: -### 概述 +| 字段类型 | 转换器 | 说明 | +| --- | --- | --- | +| `File` | `FileImageConverter` | 磁盘上的文件。 | +| `InputStream` | `InputStreamImageConverter` | 会被读取到末尾,关闭流仍由调用方负责。 | +| `byte[]`、`Byte[]` | `ByteArrayImageConverter`、`BoxingByteArrayImageConverter` | 图片的原始字节。 | +| `URL` | `UrlImageConverter` | 写文件时通过网络下载,见 [URL 来源](#url-来源)。 | +| `String` | 默认没有 | 必须显式指定,见下文。 | -支持通过文件、流、字节数组、URL 等多种方式导出图片。 +`String` 是唯一必须显式声明转换器的来源,否则该字段会被当作文本写出。根据取值选择对应的转换器: -#### POJO 类 +- `StringImageConverter` 或 `StringPathnameImageConverter` - 文件路径(两者行为一致)。 +- `StringBase64ImageConverter` - base64 数据,可以带 `data:image/png;base64,` 前缀,也可以不带。 + +## 图片导出 + +### POJO 类 ```java @Getter @@ -22,36 +39,164 @@ title: '图片' @ContentRowHeight(100) @ColumnWidth(25) public class ImageDemoData { - private File file; - private InputStream inputStream; - @ExcelProperty(converter = StringImageConverter.class) - private String string; - private byte[] byteArray; - private URL url; + private File image; } ``` -#### 代码示例 +### 代码示例 ```java @Test -public void imageWrite() throws Exception { +public void imageWrite() { String fileName = "imageWrite" + System.currentTimeMillis() + ".xlsx"; String imagePath = "path/to/image.jpg"; - List list = new ArrayList<>(); ImageDemoData data = new ImageDemoData(); - data.setFile(new File(imagePath)); - data.setByteArray(Files.readAllBytes(Paths.get(imagePath))); - data.setUrl(new URL("https://example.com/image.jpg")); - list.add(data); + data.setImage(new File(imagePath)); FesodSheet.write(fileName, ImageDemoData.class) .sheet() - .doWrite(list); + .doWrite(Collections.singletonList(data)); +} +``` + +### 结果 + +没有用 `@ExcelProperty` 指定标题时,列名就是字段名。 + + + + + + + +
A
1image
2图片
+ +指定不同的来源方式: + +```java +private InputStream image; // 也可以是 byte[]、Byte[]、URL + +@ExcelProperty(converter = StringImageConverter.class) +private String image; // String 需要显式指定转换器 +``` + +## 单个单元格内的多张图片与文字 + +`WriteCellData` 字段可以携带一个 `ImageData` 列表,从而在一个单元格内同时放置文字和多张图片。每张图片通过 `top`/`right`/`bottom`/`left` 边距(单位为磅)定位,`relativeLastColumnIndex` 可以让图片延伸到右侧的列。 + +### POJO 类 + +```java +@Getter +@Setter +@EqualsAndHashCode +@ContentRowHeight(100) +@ColumnWidth(25) +public class ImageCellDemoData { + private WriteCellData image; +} +``` + +### 代码示例 + +```java +@Test +public void imageCellWrite() throws Exception { + String fileName = "imageCellWrite" + System.currentTimeMillis() + ".xlsx"; + byte[] imageBytes = Files.readAllBytes(Paths.get("path/to/image.jpg")); + + WriteCellData writeCellData = new WriteCellData<>(); + // 如果单元格不需要文字,可以设置为 CellDataTypeEnum.EMPTY + writeCellData.setType(CellDataTypeEnum.STRING); + writeCellData.setStringValue("额外的放一些文字"); + + List imageDataList = new ArrayList<>(); + writeCellData.setImageDataList(imageDataList); + + // 第一张图片:位于单元格内部,并与右边缘保持距离 + ImageData imageData = new ImageData(); + imageDataList.add(imageData); + imageData.setImage(imageBytes); + imageData.setTop(5); + imageData.setRight(95); + imageData.setBottom(5); + imageData.setLeft(5); + + // 第二张图片:起点更靠右,并延伸到下一列 + imageData = new ImageData(); + imageDataList.add(imageData); + imageData.setImage(imageBytes); + imageData.setTop(5); + imageData.setRight(5); + imageData.setBottom(5); + imageData.setLeft(50); + // 结束位置相对当前单元格向右移动一列,图片会同时覆盖这两个单元格 + imageData.setRelativeLastColumnIndex(1); + + ImageCellDemoData data = new ImageCellDemoData(); + data.setImage(writeCellData); + + FesodSheet.write(fileName, ImageCellDemoData.class) + .sheet() + .doWrite(Collections.singletonList(data)); } ``` +图片格式支持自动识别,因此不需要设置 `ImageData.imageType`。 +边距设置如果超过单元格大小时,打开文件可能会出现修复提示。 + ### 结果 -![img](/img/docs/write/imgWrite.png) +`A` 列同时包含文字和两张图片,其中第二张图片覆盖到了 `B` 列。 + + + + + + + +
AB
1image
2图片图片额外的放一些文字
+ +## URL 来源 + +写入文件时将通过 `URL` 下载,但内置了如下的一些安全策略: + +- 默认只允许 `http` 和 `https`; +- 拒绝解析到回环、链路本地、站点本地等私有地址的主机; +- 最多跟随 3 次重定向,最多读取 10 MB; +- 连接超时为 1 秒,读取超时为 5 秒。 + +不满足上述约束时间会下载失败,且抛出自定义明细错误信息的 `IOException` 异常: + +```shell +URL image protocol is not allowed + +URL image host resolves to a restricted address + +URL image request exceeded redirect limit + +URL image data exceeds maximum size +``` + +支持自定义设置安全策略: + +```java +@Test +public void configureUrlImages() { + UrlImageConverter.setFetchPolicy(UrlImageFetchPolicy.builder() + .maxImageBytes(2 * 1024 * 1024) + .maxRedirects(1) + // 只设置 allowPrivateNetwork 不会放行任何地址, + // 还需要把主机或其网段列出来 + .allowPrivateNetwork(true) + .allowedPrivateHosts(Collections.singletonList("images.internal")) + .allowedPrivateCidrs(Collections.singletonList(CidrBlock.parse("10.0.0.0/8"))) + .build()); + + UrlImageConverter.urlConnectTimeout = 2000; + UrlImageConverter.urlReadTimeout = 10000; +} +``` + +调用 `UrlImageConverter.resetFetchPolicy()` 可以恢复默认值。 diff --git a/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/merge.md b/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/merge.md index cc0c6f115..0953a1c32 100644 --- a/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/merge.md +++ b/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/merge.md @@ -50,14 +50,18 @@ public void annotationMergeWrite() { ### 效果 -```text -| 字符串标题 | 日期标题 | 数字标题 | -|-----------|---------------------|---------| -| String0 | 2025-01-01 00:00:00 | 0.56 | -| (已合并) | 2025-01-01 00:00:00 | 0.56 | -| String1 | 2025-01-01 00:00:00 | 0.56 | -| (已合并) | 2025-01-01 00:00:00 | 0.56 | -``` +
+ + + + + + + + + +
ABC
1字符串标题日期标题数字标题
2字符串02026-07-31 20:50:230.56
32026-07-31 20:50:230.56
4字符串12026-07-31 20:50:230.56
52026-07-31 20:50:230.56
+
--- diff --git a/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/pojo.md b/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/pojo.md index dbe92ba25..bf9ca5ce6 100644 --- a/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/pojo.md +++ b/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/pojo.md @@ -13,42 +13,80 @@ title: '实体类' 通过设置列名集合动态选择要导出的列,支持忽略列或仅导出特定列。 +集合中填写的是 **POJO 的字段名**,而非表头标题。下面两个示例都使用 [简单写入](./simple.md) 中的 `DemoData` 类和 `data()` +方法,其字段为 `string`、`date` 和 `doubleData`。 + ### 代码示例 -忽略指定列 +#### 忽略指定列 -```java +除列出的字段外,其余字段都会写入: +```java @Test -public void excludeOrIncludeWrite() { +public void excludeColumnWrite() { String fileName = "excludeColumnFieldWrite" + System.currentTimeMillis() + ".xlsx"; - Set excludeColumns = Set.of("date"); + Set excludeColumns = Collections.singleton("date"); FesodSheet.write(fileName, DemoData.class) - .excludeColumnFieldNames(excludeColumns) - .sheet() - .doWrite(data()); + .excludeColumnFieldNames(excludeColumns) + .sheet() + .doWrite(data()); } ``` -仅导出指定列 +**结果** + +`date` 字段被去掉,其余两列保留: + +
+ + + + + + + + + + +
AB
1字符串标题数字标题
2字符串00.56
3字符串10.56
4字符串20.56
11字符串90.56
+
+ +#### 仅导出指定列 + +只有列出的字段会写入: ```java @Test -public void excludeOrIncludeWrite() { - String fileName = "includeColumnFiledWrite" + System.currentTimeMillis() + ".xlsx"; +public void includeColumnWrite() { + String fileName = "includeColumnFieldWrite" + System.currentTimeMillis() + ".xlsx"; - Set includeColumns = Set.of("date"); + Set includeColumns = Collections.singleton("date"); FesodSheet.write(fileName, DemoData.class) - .includeColumnFiledNames(includeColumns) + .includeColumnFieldNames(includeColumns) .sheet() .doWrite(data()); } ``` -### 结果 +**结果** + +只保留 `date` 字段: -![img](/img/docs/write/excludeOrIncludeWrite.png) +
+ + + + + + + + + + +
A
1日期标题
22026-07-31 20:50:23
32026-07-31 20:50:23
42026-07-31 20:50:23
112026-07-31 20:50:23
+
--- @@ -56,7 +94,7 @@ public void excludeOrIncludeWrite() { ### 概述 -通过 `@ExcelProperty` 注解的 `index` 属性指定列顺序。 +指定写入列顺序。 ### POJO 类 @@ -77,6 +115,10 @@ public class IndexData { ### 代码示例 +#### `index` 属性 + +通过 `@ExcelProperty` 注解的 `index` 属性指定列顺序。 + ```java @Test public void indexWrite() { @@ -87,9 +129,89 @@ public void indexWrite() { } ``` -### 结果 +**结果** + +
+ + + + + + + + + + +
ABCD
1字符串标题日期标题数字标题
2字符串02026-07-31 20:50:230.56
3字符串12026-07-31 20:50:230.56
4字符串22026-07-31 20:50:230.56
11字符串92026-07-31 20:50:230.56
+
+ +:::note +**C 列的空白是刻意为之。** `index` 是从 0 开始的绝对列位置,而不是排序键:示例中三个字段声明的是 `0`、`1` 和 `3`,因此位置 +`2` 上没有写入任何内容,输出中就保留了这一处空列。若希望三列紧挨在一起,请把它们编号为 `0`、`1`、`2`。 +::: + +#### includeColumnFieldNames + +列的顺序取决于 POJO,而不是集合。即使把 `doubleData` 写在 `string` 前面,输出中仍然是 `string` 在前,因为字段就是按这个顺序声明的: + +```java +@Test +public void includeColumnOrderWrite() { + String fileName = "includeColumnFieldWrite" + System.currentTimeMillis() + ".xlsx"; + + Set includeColumns = new LinkedHashSet<>(Arrays.asList("doubleData", "string")); + FesodSheet.write(fileName, DemoData.class) + .includeColumnFieldNames(includeColumns) + .sheet() + .doWrite(data()); +} +``` + +**结果** + +
+ + + + + + + +
AB
1字符串标题数字标题
2字符串00.56
+
+ +#### orderByIncludeColumn + +加上 `.orderByIncludeColumn(true)`,即可改为按集合的顺序排列: + +```java +@Test +public void orderByIncludeColumnWrite() { + String fileName = "includeColumnFieldWrite" + System.currentTimeMillis() + ".xlsx"; + + Set includeColumns = new LinkedHashSet<>(Arrays.asList("doubleData", "string")); + FesodSheet.write(fileName, DemoData.class) + .includeColumnFieldNames(includeColumns) + .orderByIncludeColumn(true) + .sheet() + .doWrite(data()); +} +``` + +**结果** + +
+ + + + + + + +
AB
1数字标题字符串标题
20.56字符串0
+
-![img](/img/docs/write/indexWrite.png) +此时集合的迭代顺序必须稳定 - 用 `LinkedHashSet` 或 `List`,不要用 `HashSet`。 --- @@ -130,4 +252,16 @@ private List> dataList() { ### 结果 -![img](/img/docs/write/noModelWrite.png) +
+ + + + + + + + + + +
ABC
1字符串标题数字标题日期标题
2字符串00.562026-07-31 20:50:23
3字符串10.562026-07-31 20:50:23
4字符串20.562026-07-31 20:50:23
11字符串90.562026-07-31 20:50:23
+
diff --git a/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/sheet.md b/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/sheet.md index f4604f460..f7bdd9a1e 100644 --- a/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/sheet.md +++ b/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/sheet.md @@ -34,7 +34,20 @@ public void writeSingleSheet() { ### 结果 -![img](/img/docs/write/repeatedWrite.png) +
+ + + + + + + + + + +
ABC
1字符串标题日期标题数字标题
2字符串02026-07-31 20:50:230.56
3字符串12026-07-31 20:50:230.56
4字符串22026-07-31 20:50:230.56
51字符串92026-07-31 20:50:230.56
+
Sheet1
+
--- @@ -63,7 +76,20 @@ public void writeMultiSheet() { ### 结果 -![img](/img/docs/write/repeatedWrite.png) +
+ + + + + + + + + + +
ABC
1字符串标题日期标题数字标题
2字符串02026-07-31 20:50:230.56
3字符串12026-07-31 20:50:230.56
4字符串22026-07-31 20:50:230.56
11字符串92026-07-31 20:50:230.56
+
Sheet0Sheet1Sheet2Sheet3Sheet4
+
--- @@ -81,10 +107,10 @@ public void writeMultiSheet() { public void tableWrite() { String fileName = "tableWrite" + System.currentTimeMillis() + ".xlsx"; - try (ExcelWriter excelWriter = FesodSheet.write(fileName, DemoData.class).build()) { + try (ExcelWriter excelWriter = FesodSheet.write(fileName).build()) { WriteSheet writeSheet = FesodSheet.writerSheet("Table示例").build(); - WriteTable table1 = FesodSheet.writerTable(0).build(); - WriteTable table2 = FesodSheet.writerTable(1).build(); + WriteTable table1 = FesodSheet.writerTable(0).head(DemoData.class).build(); + WriteTable table2 = FesodSheet.writerTable(1).head(DemoData.class).build(); excelWriter.write(data(), writeSheet, table1); excelWriter.write(data(), writeSheet, table2); @@ -94,4 +120,32 @@ public void tableWrite() { ### 结果 -![img](/img/docs/write/tableWrite.png) +
+ + + + + + + + + + + + + + + + +
ABC
1字符串标题日期标题数字标题
2字符串02026-07-31 20:50:230.56
3字符串12026-07-31 20:50:230.56
4字符串22026-07-31 20:50:230.56
11字符串92026-07-31 20:50:230.56
12字符串标题日期标题数字标题
13字符串02026-07-31 20:50:230.56
14字符串12026-07-31 20:50:230.56
15字符串22026-07-31 20:50:230.56
22字符串92026-07-31 20:50:230.56
+
Table示例
+
+ +:::note +每个 `WriteTable` 各写入一行自己的表头,因此表头分别出现在第 1 行和第 12 行,两段各 10 行数据依次位于 +2-11 行和 13-22 行。 + +这里把表头声明在每个 `WriteTable` 上,而不是在 Writer 上;当各个 Table 的数据类型不同时也正需要这种写法。 +如果改用 `FesodSheet.write(fileName, DemoData.class)` 构建 Writer,Sheet 自身也会有表头, +会在第一个 Table 的表头之上多写一行。 +::: diff --git a/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/simple.md b/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/simple.md index 6e8afc928..b53090518 100644 --- a/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/simple.md +++ b/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/simple.md @@ -41,7 +41,7 @@ private List data() { List list = ListUtils.newArrayList(); for (int i = 0; i < 10; i++) { DemoData data = new DemoData(); - data.setString("String" + i); + data.setString("字符串" + i); data.setDate(new Date()); data.setDoubleData(0.56); list.add(data); @@ -96,3 +96,23 @@ public void simpleWrite() { } } ``` + +### 结果 + +三种方式生成的文件完全相同。 +第 1 行是取自 `@ExcelProperty` 的表头标题,`data()` 返回的多个数据对象依次写入第 2 至 11 行。 +`ignore` 字段因为标注了 `@ExcelIgnore` 而不会出现。 + +
+ + + + + + + + + + +
ABC
1字符串标题日期标题数字标题
2字符串02026-07-31 20:50:230.56
3字符串12026-07-31 20:50:230.56
4字符串22026-07-31 20:50:230.56
11字符串92026-07-31 20:50:230.56
+
diff --git a/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/style.md b/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/style.md index 8d2a33bb0..dd20042e1 100644 --- a/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/style.md +++ b/website/i18n/zh-cn/docusaurus-plugin-content-docs/current/sheet/write/style.md @@ -60,7 +60,19 @@ public void annotationStyleWrite() { ### 结果 -![img](/img/docs/write/annotationStyleWrite.png) +
+ + + + + + + + + + +
ABC
1字符串标题日期标题数字标题
2字符串02026-07-31 20:50:230.56
3字符串12026-07-31 20:50:230.56
4字符串22026-07-31 20:50:230.56
11字符串92026-07-31 20:50:230.56
+
--- @@ -106,7 +118,19 @@ public void handlerStyleWrite() { ### 结果 -![img](/img/docs/write/handlerStyleWrite.png) +
+ + + + + + + + + + +
ABC
1字符串标题日期标题数字标题
2字符串02026-07-31 20:50:230.56
3字符串12026-07-31 20:50:230.56
4字符串22026-07-31 20:50:230.56
11字符串92026-07-31 20:50:230.56
+
--- @@ -245,4 +269,16 @@ public void widthAndHeightWrite() { ### 结果 -![img](/img/docs/write/widthAndHeightWrite.png) +
+ + + + + + + + + + +
ABC
1字符串标题日期标题数字标题
2字符串02026-07-31 20:50:230.56
3字符串12026-07-31 20:50:230.56
4字符串22026-07-31 20:50:230.56
11字符串92026-07-31 20:50:230.56
+
diff --git a/website/src/css/xl-sheet.css b/website/src/css/xl-sheet.css new file mode 100644 index 000000000..fe37fd4f0 --- /dev/null +++ b/website/src/css/xl-sheet.css @@ -0,0 +1,413 @@ +/* + * 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. + */ + +/* + * Excel-like grids used in the write/fill docs in place of screenshots. + * + * Geometry is expressed in Excel's own units and scaled by --xl-scale. Each + * spreadsheet value gets its own class, named after that value: + * + * @ColumnWidth(25) + * @ContentRowHeight(30) + * setFontHeightInPoints(20) + * + * + * + * The values have to be enumerated here rather than passed inline, because an + * MDX `style` attribute is parsed by hast-util-to-estree via style-to-js, and + * the version this site resolves throws on any `style` attribute at all. + */ + +.xl-sheet-container, +.xl-sheet, +.xl-sheet-tabs { + --xl-scale: 1; + --xl-col-unit: calc(7.2px * var(--xl-scale)); /* one Excel column-width unit */ + --xl-row-unit: calc(1.8px * var(--xl-scale)); /* one point of Excel row height */ + --xl-pt: calc(0.8pt * var(--xl-scale)); + + --xl-paper: #f5f5f5; + --xl-ink: #000; + --xl-grid: #e0e0e0; + --xl-head-bg: #bfbfbf; + --xl-chrome-bg: #f5f5f5; + --xl-chrome-grid: #e0e0e0; + --xl-tab-bg: #f5f5f5; + --xl-tab-fg: #555; + + --xl-white: #ffffff; + --xl-red: #ff0000; + --xl-magenta: #ff00ff; + --xl-green: #14824b; + --xl-sky: #1890ff; + --xl-bright-green: #168f52; + + font-family: system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", Helvetica, sans-serif, Arial; + line-height: 1.2; +} + +/* dark theme */ + +[data-theme='dark'] { + .xl-sheet-container { + border: none; + background: rgba(255, 255, 255, 0.05); + } + + .xl-sheet .xl-chrome { + background: rgba(255, 255, 255, 0.05); + color: var(--xl-white); + border-color: rgba(255, 255, 255, 0.05); + } + + .xl-sheet .xl-head { + border-color: var(--xl-grid); + } + + .xl-sheet-tabs td { + background: transparent; + color: #e8e8e8; + } + + .xl-sheet-tabs .xl-tab-active { + background: rgba(255, 255, 255, 0.05); + color: var(--xl-white); + border-bottom-color: #00b95f; + } + + .xl-sheet td:not(.xl-chrome):not(.xl-head) { + border-top: none; + border-bottom: none; + } +} + +/* Container */ + +.xl-sheet-container { + width: fit-content; + overflow-x: auto; + font-size: calc(13 * var(--xl-pt)); + background: var(--xl-paper); + border: 1px solid var(--xl-grid); + margin-bottom: calc(10 * var(--xl-row-unit)); +} + +/* Grid */ + +.xl-sheet { + /* Infima sets `table { display: block }`, which stretches the paper + background across the full content width; shrink-to-fit instead. */ + width: fit-content; + max-width: 100%; +} + +.xl-sheet tr { + border-top: none; + border-bottom: 1px solid var(--xl-grid); +} + +.xl-sheet tr:first-child { + border-top: none; +} + +.xl-sheet tr:first-child td { + border-top: none; +} + +.xl-sheet tr:last-child { + border-bottom: none; +} + +.xl-sheet td { + min-width: calc(8 * var(--xl-col-unit)); + height: calc(15 * var(--xl-row-unit)); + padding: 0 calc(0.85 * var(--xl-col-unit)); + border-right: 1px solid var(--xl-grid); + white-space: nowrap; +} + +.xl-sheet td:not(.xl-chrome):not(.xl-head) { + height: calc(15 * var(--xl-row-unit)); + border-top: none; + background: var(--xl-white); + color: #333; +} + +.xl-sheet td:first-child { + border-left: none; +} + +.xl-sheet td:last-child { + border-right: none; +} + +.xl-sheet a { + color: var(--xl-sky); + text-decoration: underline; +} + +.xl-sheet td:not(.xl-chrome):empty { + min-width: calc(10.5 * var(--xl-col-unit)); + background: var(--xl-white); +} + +/* Cell roles */ + +.xl-sheet .xl-chrome { + min-width: calc(5 * var(--xl-col-unit)); + background: var(--xl-chrome-bg); + border-color: var(--xl-chrome-grid); + color: #333; + text-align: center; +} + +.xl-sheet .xl-head { + background: var(--xl-head-bg); + padding: 1px calc(0.85 * var(--xl-col-unit)); + font-weight: 500; + color: #333; + white-space: normal; + vertical-align: middle; + text-align: center; +} + +.xl-sheet .xl-num { + text-align: right; +} + +.xl-sheet .xl-muted { + color: #999; + text-align: center; +} + +/* Cell modifiers, one per spreadsheet value in use */ + +.xl-sheet .xl-cw-25 { + width: calc(25 * var(--xl-col-unit)); +} + +.xl-sheet .xl-cw-50 { + width: calc(50 * var(--xl-col-unit)); +} + +.xl-sheet .xl-rh-20 { + height: calc(20 * var(--xl-row-unit)); +} + +.xl-sheet .xl-rh-30 { + height: calc(30 * var(--xl-row-unit)); +} + +.xl-sheet .xl-rh-100 { + height: calc(100 * var(--xl-row-unit)); +} + +.xl-sheet .xl-fs-20 { + font-size: calc(20 * var(--xl-pt)); +} + +.xl-sheet .xl-fs-30 { + font-size: calc(30 * var(--xl-pt)); +} + +.xl-sheet .xl-fill-red { + background: var(--xl-red); +} + +.xl-sheet .xl-fill-magenta { + background: var(--xl-magenta); +} + +.xl-sheet .xl-fill-green { + background: var(--xl-green); +} + +.xl-sheet .xl-fill-sky { + background: var(--xl-sky); +} + +.xl-sheet .xl-fill-bright-green { + background: var(--xl-bright-green); +} + +.xl-sheet .xl-fc-red { + color: var(--xl-red); +} + +/* Pictures */ + +.xl-sheet .xl-pic, +.xl-sheet .xl-pic-multi { + padding: 0; +} + +/* Fesod anchors the picture to exactly fill its cell, so it is stretched + to the cell box rather than keeping its aspect ratio. */ +.xl-sheet .xl-pic img { + display: block; + width: 100%; + height: 100%; +} + +.xl-sheet .xl-pic-multi { + position: relative; +} + +.xl-sheet .xl-pic-multi > b { + position: absolute; + left: 6px; + bottom: 2px; + font-weight: normal; +} + +/* Both axes are given: an absolutely positioned with `width: auto` + falls back to its intrinsic size and ignores `right`. */ +.xl-sheet .xl-pic-abs { + position: absolute; + top: 7px; + height: 150px; +} + +/* The second picture starts further right and runs into the next column. */ +.xl-sheet .xl-pic-abs-left { + left: 7px; + width: 51px; +} + +.xl-sheet .xl-pic-abs-right { + left: 65px; + width: 182px; +} + +/* Overlays */ + +/* Comments and dropdowns are drawn open, as a screenshot would show them, and + they reach outside the grid. Infima's `table { overflow: auto }` would clip + them, so a sheet carrying one opts out of scrolling. */ +.xl-sheet--overlay { + overflow: visible; +} + +.xl-sheet .xl-comment-anchor, +.xl-sheet .xl-dropdown { + position: relative; +} + +.xl-sheet .xl-comment-anchor::after { + content: ""; + position: absolute; + top: 0; + right: 0; + border-top: 8px solid #d93025; + border-left: 8px solid transparent; +} + +.xl-sheet .xl-comment { + position: absolute; + top: 55%; + left: calc(100% + 4px); + z-index: 2; + padding: 2px 8px; + background: #ffffe1; + border: 1px solid #b7b7b7; + box-shadow: 2px 2px 3px rgba(0, 0, 0, .25); + font-size: calc(13 * var(--xl-pt)); + font-weight: normal; + white-space: nowrap; + text-align: left; +} + +.xl-sheet .xl-dropdown-btn { + position: absolute; + top: 0; + right: 0; + bottom: 0; + width: 22px; + display: flex; + align-items: center; + justify-content: center; + background: var(--xl-tab-bg); + border: none; + font-size: calc(11 * var(--xl-pt)); + font-weight: normal; +} + +.xl-sheet .xl-dropdown-list { + position: absolute; + top: 100%; + left: 0; + z-index: 2; + min-width: 100%; + background: var(--xl-paper); + border: none; + box-shadow: 1px 2px 4px rgba(0, 0, 0, .25); + font-weight: normal; +} + +.xl-sheet .xl-dropdown-list > b { + display: block; + padding: 4px 6px; + font-weight: normal; +} + +/* Sheet tabs, rendered directly under a .xl-sheet grid */ + +.xl-sheet:has(+ .xl-sheet-tabs) { + margin-bottom: 0; +} + +/* A table rather than a div, because the docs may only use the elements + markdownlint's MD033 allowlist permits. The tabs still lay out as a flex + row, so the table's own box model is switched off. */ +.xl-sheet-tabs { + display: block; + width: fit-content; + max-width: 100%; + border: 0; + margin-bottom: 0; +} + +.xl-sheet-tabs tbody { + display: block; +} + +.xl-sheet-tabs tr { + display: flex; + flex-wrap: wrap; + gap: 0; + background: transparent; +} + +.xl-sheet-tabs tr:first-child { + border-top: none; +} + +.xl-sheet-tabs td { + padding: 6px 8px; + border: 0; + background: var(--xl-tab-bg); + color: var(--xl-tab-fg); +} + +.xl-sheet-tabs .xl-tab-active { + background: var(--xl-paper); + color: var(--xl-ink); + border-bottom: 2px solid var(--xl-green); + font-weight: 500; +} \ No newline at end of file diff --git a/website/static/img/docs/fill/complexFillWithTable_file.png b/website/static/img/docs/fill/complexFillWithTable_file.png deleted file mode 100644 index a87a3e756..000000000 Binary files a/website/static/img/docs/fill/complexFillWithTable_file.png and /dev/null differ diff --git a/website/static/img/docs/fill/complexFillWithTable_result.png b/website/static/img/docs/fill/complexFillWithTable_result.png deleted file mode 100644 index dc27781a2..000000000 Binary files a/website/static/img/docs/fill/complexFillWithTable_result.png and /dev/null differ diff --git a/website/static/img/docs/fill/complexFill_file.png b/website/static/img/docs/fill/complexFill_file.png deleted file mode 100644 index b2540ed2c..000000000 Binary files a/website/static/img/docs/fill/complexFill_file.png and /dev/null differ diff --git a/website/static/img/docs/fill/complexFill_result.png b/website/static/img/docs/fill/complexFill_result.png deleted file mode 100644 index 0a9d289b8..000000000 Binary files a/website/static/img/docs/fill/complexFill_result.png and /dev/null differ diff --git a/website/static/img/docs/fill/compositeFill_file.png b/website/static/img/docs/fill/compositeFill_file.png deleted file mode 100644 index 0e9f099fa..000000000 Binary files a/website/static/img/docs/fill/compositeFill_file.png and /dev/null differ diff --git a/website/static/img/docs/fill/compositeFill_result.png b/website/static/img/docs/fill/compositeFill_result.png deleted file mode 100644 index 316a00227..000000000 Binary files a/website/static/img/docs/fill/compositeFill_result.png and /dev/null differ diff --git a/website/static/img/docs/fill/horizontalFill_file.png b/website/static/img/docs/fill/horizontalFill_file.png deleted file mode 100644 index 70af032d1..000000000 Binary files a/website/static/img/docs/fill/horizontalFill_file.png and /dev/null differ diff --git a/website/static/img/docs/fill/horizontalFill_result.png b/website/static/img/docs/fill/horizontalFill_result.png deleted file mode 100644 index e88053d08..000000000 Binary files a/website/static/img/docs/fill/horizontalFill_result.png and /dev/null differ diff --git a/website/static/img/docs/fill/listFill_file.png b/website/static/img/docs/fill/listFill_file.png deleted file mode 100644 index 8ebb75551..000000000 Binary files a/website/static/img/docs/fill/listFill_file.png and /dev/null differ diff --git a/website/static/img/docs/fill/listFill_result.png b/website/static/img/docs/fill/listFill_result.png deleted file mode 100644 index 4cdd60d47..000000000 Binary files a/website/static/img/docs/fill/listFill_result.png and /dev/null differ diff --git a/website/static/img/docs/fill/simpleFill_file.png b/website/static/img/docs/fill/simpleFill_file.png deleted file mode 100644 index e393bad97..000000000 Binary files a/website/static/img/docs/fill/simpleFill_file.png and /dev/null differ diff --git a/website/static/img/docs/fill/simpleFill_result.png b/website/static/img/docs/fill/simpleFill_result.png deleted file mode 100644 index 1936c0e6f..000000000 Binary files a/website/static/img/docs/fill/simpleFill_result.png and /dev/null differ diff --git a/website/static/img/docs/write/annotationStyleWrite.png b/website/static/img/docs/write/annotationStyleWrite.png deleted file mode 100644 index 6aeb9fb55..000000000 Binary files a/website/static/img/docs/write/annotationStyleWrite.png and /dev/null differ diff --git a/website/static/img/docs/write/commentWrite.png b/website/static/img/docs/write/commentWrite.png deleted file mode 100644 index 9ffcb125f..000000000 Binary files a/website/static/img/docs/write/commentWrite.png and /dev/null differ diff --git a/website/static/img/docs/write/complexHeadWrite.png b/website/static/img/docs/write/complexHeadWrite.png deleted file mode 100644 index e52d09608..000000000 Binary files a/website/static/img/docs/write/complexHeadWrite.png and /dev/null differ diff --git a/website/static/img/docs/write/converterWrite.png b/website/static/img/docs/write/converterWrite.png deleted file mode 100644 index 90196e1ab..000000000 Binary files a/website/static/img/docs/write/converterWrite.png and /dev/null differ diff --git a/website/static/img/docs/write/customHandlerWrite.png b/website/static/img/docs/write/customHandlerWrite.png deleted file mode 100644 index f263d02ac..000000000 Binary files a/website/static/img/docs/write/customHandlerWrite.png and /dev/null differ diff --git a/website/static/img/docs/write/dynamicHeadWrite.png b/website/static/img/docs/write/dynamicHeadWrite.png deleted file mode 100644 index 684303707..000000000 Binary files a/website/static/img/docs/write/dynamicHeadWrite.png and /dev/null differ diff --git a/website/static/img/docs/write/excludeOrIncludeWrite.png b/website/static/img/docs/write/excludeOrIncludeWrite.png deleted file mode 100644 index 8911e5fbd..000000000 Binary files a/website/static/img/docs/write/excludeOrIncludeWrite.png and /dev/null differ diff --git a/website/static/img/docs/write/handlerStyleWrite.png b/website/static/img/docs/write/handlerStyleWrite.png deleted file mode 100644 index 7f6248f8c..000000000 Binary files a/website/static/img/docs/write/handlerStyleWrite.png and /dev/null differ diff --git a/website/static/img/docs/write/ignore_row.png b/website/static/img/docs/write/ignore_row.png deleted file mode 100644 index 1017b472f..000000000 Binary files a/website/static/img/docs/write/ignore_row.png and /dev/null differ diff --git a/website/static/img/docs/write/imgWrite.png b/website/static/img/docs/write/imgWrite.png deleted file mode 100644 index 9ad021d51..000000000 Binary files a/website/static/img/docs/write/imgWrite.png and /dev/null differ diff --git a/website/static/img/docs/write/indexRow.png b/website/static/img/docs/write/indexRow.png deleted file mode 100644 index 0010cc07c..000000000 Binary files a/website/static/img/docs/write/indexRow.png and /dev/null differ diff --git a/website/static/img/docs/write/indexWrite.png b/website/static/img/docs/write/indexWrite.png deleted file mode 100644 index 238d9db82..000000000 Binary files a/website/static/img/docs/write/indexWrite.png and /dev/null differ diff --git a/website/static/img/docs/write/mergeWrite.png b/website/static/img/docs/write/mergeWrite.png deleted file mode 100644 index 0249c4f61..000000000 Binary files a/website/static/img/docs/write/mergeWrite.png and /dev/null differ diff --git a/website/static/img/docs/write/noModelWrite.png b/website/static/img/docs/write/noModelWrite.png deleted file mode 100644 index 6a66c62d1..000000000 Binary files a/website/static/img/docs/write/noModelWrite.png and /dev/null differ diff --git a/website/static/img/docs/write/repeatedWrite.png b/website/static/img/docs/write/repeatedWrite.png deleted file mode 100644 index df324c968..000000000 Binary files a/website/static/img/docs/write/repeatedWrite.png and /dev/null differ diff --git a/website/static/img/docs/write/sample-image.svg b/website/static/img/docs/write/sample-image.svg new file mode 100644 index 000000000..7e941a742 --- /dev/null +++ b/website/static/img/docs/write/sample-image.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/website/static/img/docs/write/tableWrite.png b/website/static/img/docs/write/tableWrite.png deleted file mode 100644 index e1b6f2a81..000000000 Binary files a/website/static/img/docs/write/tableWrite.png and /dev/null differ diff --git a/website/static/img/docs/write/templateWrite.png b/website/static/img/docs/write/templateWrite.png deleted file mode 100644 index f378efb4b..000000000 Binary files a/website/static/img/docs/write/templateWrite.png and /dev/null differ diff --git a/website/static/img/docs/write/widthAndHeightWrite.png b/website/static/img/docs/write/widthAndHeightWrite.png deleted file mode 100644 index 167b55114..000000000 Binary files a/website/static/img/docs/write/widthAndHeightWrite.png and /dev/null differ diff --git a/website/static/img/docs/write/writeCellDataWrite.png b/website/static/img/docs/write/writeCellDataWrite.png deleted file mode 100644 index 54b2666f2..000000000 Binary files a/website/static/img/docs/write/writeCellDataWrite.png and /dev/null differ