fix: 优化word文档导出
This commit is contained in:
parent
1c80c33bd6
commit
c1ede68b1a
@ -1,17 +1,23 @@
|
|||||||
package com.yfd.platform.qgc_export.controller;
|
package com.yfd.platform.qgc_export.controller;
|
||||||
|
|
||||||
import com.yfd.platform.qgc_export.domain.word.WordChartExportRequest;
|
import com.yfd.platform.qgc_export.domain.word.WordChartExportRequest;
|
||||||
|
import com.yfd.platform.qgc_export.domain.word.WordChartVo;
|
||||||
|
import com.yfd.platform.qgc_export.domain.word.WordSeriesVo;
|
||||||
import com.yfd.platform.qgc_export.service.IWordChartExportService;
|
import com.yfd.platform.qgc_export.service.IWordChartExportService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Word 图表导出控制器
|
* Word 图表导出控制器
|
||||||
* <p>
|
* <p>
|
||||||
@ -37,4 +43,133 @@ public class WordChartExportController {
|
|||||||
public void exportWord(@RequestBody WordChartExportRequest request, HttpServletResponse response) {
|
public void exportWord(@RequestBody WordChartExportRequest request, HttpServletResponse response) {
|
||||||
wordChartExportService.exportWord(request, response);
|
wordChartExportService.exportWord(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回一份带多种图表、多系列、自定义颜色的模拟请求数据,便于前端参考或直接复制。
|
||||||
|
*/
|
||||||
|
@GetMapping("/demo")
|
||||||
|
@Operation(summary = "获取模拟导出请求数据(含 6 种图表类型 + 自定义颜色)")
|
||||||
|
public WordChartExportRequest demo() {
|
||||||
|
return buildDemoRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
private WordChartExportRequest buildDemoRequest() {
|
||||||
|
WordChartExportRequest request = new WordChartExportRequest();
|
||||||
|
request.setTitle("年度水电监测数据分析报告");
|
||||||
|
request.setSubtitle("演示数据(含饼图/柱状图/折线图/折柱混合/堆叠/归一化堆叠)");
|
||||||
|
|
||||||
|
List<String> months = Arrays.asList("1月", "2月", "3月", "4月", "5月", "6月");
|
||||||
|
|
||||||
|
// 1. 饼图:各站类设备数量占比(相邻分片留白 + 自定义颜色)
|
||||||
|
WordChartVo pie = new WordChartVo();
|
||||||
|
pie.setType("pie");
|
||||||
|
pie.setHeading("第一章 设备总体情况");
|
||||||
|
pie.setSubHeading("1.1 各站类设备数量占比");
|
||||||
|
pie.setTitle("各站类设备数量占比");
|
||||||
|
pie.setDescription("图 1-1 各站类设备数量占比示意图");
|
||||||
|
pie.setCategories(Arrays.asList("水质站", "水文站", "水温站", "视频设备", "AI分拣装置"));
|
||||||
|
pie.setColors(Arrays.asList("#4C72B0", "#DD8452", "#55A868", "#C44E52", "#8172B2"));
|
||||||
|
pie.setPieGap(3);
|
||||||
|
WordSeriesVo pieSeries = new WordSeriesVo();
|
||||||
|
pieSeries.setName("数量");
|
||||||
|
pieSeries.setValues(Arrays.asList(120.0, 85.0, 60.0, 45.0, 30.0));
|
||||||
|
pie.setSeries(List.of(pieSeries));
|
||||||
|
|
||||||
|
// 2. 柱状图:多系列 + 自定义颜色
|
||||||
|
WordChartVo bar = new WordChartVo();
|
||||||
|
bar.setType("bar");
|
||||||
|
bar.setHeading("第二章 基地设备分布");
|
||||||
|
bar.setSubHeading("2.1 各基地设备数量对比");
|
||||||
|
bar.setTitle("各基地设备数量对比");
|
||||||
|
bar.setDescription("图 2-1 各基地设备数量对比示意图");
|
||||||
|
bar.setXAxisName("基地");
|
||||||
|
bar.setYAxisName("数量(台)");
|
||||||
|
bar.setCategories(Arrays.asList("班多基地", "羊曲基地", "茨哈峡基地", "龙羊峡基地"));
|
||||||
|
WordSeriesVo bar1 = series("智能装备", "#4C72B0", 120.0, 90.0, 150.0, 80.0);
|
||||||
|
WordSeriesVo bar2 = series("监测设备", "#55A868", 60.0, 70.0, 95.0, 110.0);
|
||||||
|
WordSeriesVo bar3 = series("视频设备", "#DD8452", 40.0, 50.0, 60.0, 70.0);
|
||||||
|
bar.setSeries(Arrays.asList(bar1, bar2, bar3));
|
||||||
|
|
||||||
|
// 3. 折线图:月度趋势
|
||||||
|
WordChartVo line = new WordChartVo();
|
||||||
|
line.setType("line");
|
||||||
|
line.setHeading("第三章 生态流量趋势");
|
||||||
|
line.setSubHeading("3.1 月度达标率趋势");
|
||||||
|
line.setTitle("月度生态流量达标率趋势");
|
||||||
|
line.setDescription("图 3-1 2024/2025 年月度生态流量达标率变化趋势");
|
||||||
|
line.setXAxisName("月份");
|
||||||
|
line.setYAxisName("达标率(%)");
|
||||||
|
line.setCategories(months);
|
||||||
|
WordSeriesVo line1 = series("2024年", "#C44E52", 92.0, 94.0, 90.0, 95.0, 96.0, 93.0);
|
||||||
|
WordSeriesVo line2 = series("2025年", "#4C72B0", 88.0, 91.0, 93.0, 92.0, 97.0, 98.0);
|
||||||
|
line.setSeries(Arrays.asList(line1, line2));
|
||||||
|
|
||||||
|
// 4. 折柱混合图:柱(主轴)+ 折线(副轴)
|
||||||
|
WordChartVo barLine = new WordChartVo();
|
||||||
|
barLine.setType("barLine");
|
||||||
|
barLine.setHeading("第四章 水量水质综合");
|
||||||
|
barLine.setSubHeading("4.1 水量与浊度综合趋势");
|
||||||
|
barLine.setTitle("水量与水质综合趋势");
|
||||||
|
barLine.setDescription("图 4-1 下泄水量(柱)与浊度(折线)综合趋势");
|
||||||
|
barLine.setXAxisName("月份");
|
||||||
|
barLine.setYAxisName("下泄水量(万m³)");
|
||||||
|
barLine.setYAxisNameRight("浊度(NTU)");
|
||||||
|
barLine.setCategories(months);
|
||||||
|
WordSeriesVo barSeries = series("下泄水量", "#55A868", 0, 320.0, 350.0, 410.0, 500.0, 620.0, 780.0);
|
||||||
|
WordSeriesVo lineSeries = series("浊度", "#C44E52", 1, 12.0, 11.0, 9.0, 8.0, 6.0, 5.0);
|
||||||
|
barLine.setSeries(Arrays.asList(barSeries, lineSeries));
|
||||||
|
|
||||||
|
// 5. 堆叠柱状图
|
||||||
|
WordChartVo stacked = new WordChartVo();
|
||||||
|
stacked.setType("stackedBar");
|
||||||
|
stacked.setHeading("第五章 基地设备构成");
|
||||||
|
stacked.setSubHeading("5.1 设备类型构成");
|
||||||
|
stacked.setTitle("各基地设备类型构成");
|
||||||
|
stacked.setDescription("图 5-1 各基地设备类型构成堆叠图");
|
||||||
|
stacked.setXAxisName("基地");
|
||||||
|
stacked.setYAxisName("数量(台)");
|
||||||
|
stacked.setCategories(Arrays.asList("班多基地", "羊曲基地", "茨哈峡基地"));
|
||||||
|
stacked.setSeries(Arrays.asList(
|
||||||
|
series("水质", "#4C72B0", 30.0, 20.0, 40.0),
|
||||||
|
series("水文", "#55A868", 25.0, 35.0, 30.0),
|
||||||
|
series("视频", "#DD8452", 15.0, 25.0, 20.0)
|
||||||
|
));
|
||||||
|
|
||||||
|
// 6. 归一化堆叠柱状图(百分比)
|
||||||
|
WordChartVo percent = new WordChartVo();
|
||||||
|
percent.setType("percentStackedBar");
|
||||||
|
percent.setHeading("第五章 基地设备构成");
|
||||||
|
percent.setSubHeading("5.2 设备类型占比(归一化)");
|
||||||
|
percent.setTitle("各基地设备类型占比(归一化)");
|
||||||
|
percent.setDescription("图 5-2 各基地设备类型占比归一化堆叠图");
|
||||||
|
percent.setXAxisName("基地");
|
||||||
|
percent.setYAxisName("占比(%)");
|
||||||
|
percent.setCategories(Arrays.asList("班多基地", "羊曲基地", "茨哈峡基地"));
|
||||||
|
percent.setSeries(Arrays.asList(
|
||||||
|
series("水质", "#4C72B0", 30.0, 20.0, 40.0),
|
||||||
|
series("水文", "#55A868", 25.0, 35.0, 30.0),
|
||||||
|
series("视频", "#DD8452", 15.0, 25.0, 20.0)
|
||||||
|
));
|
||||||
|
|
||||||
|
request.setCharts(Arrays.asList(pie, bar, line, barLine, stacked, percent));
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private WordSeriesVo series(String name, String color, double... values) {
|
||||||
|
WordSeriesVo vo = new WordSeriesVo();
|
||||||
|
vo.setName(name);
|
||||||
|
vo.setColor(color);
|
||||||
|
List<Double> list = new java.util.ArrayList<>();
|
||||||
|
for (double v : values) {
|
||||||
|
list.add(v);
|
||||||
|
}
|
||||||
|
vo.setValues(list);
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private WordSeriesVo series(String name, String color, int yAxisIndex, double... values) {
|
||||||
|
WordSeriesVo vo = series(name, color, values);
|
||||||
|
vo.setYAxisIndex(yAxisIndex);
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,9 +16,18 @@ public class WordChartVo {
|
|||||||
@Schema(description = "图表类型:pie/bar/line/barLine/stackedBar/percentStackedBar")
|
@Schema(description = "图表类型:pie/bar/line/barLine/stackedBar/percentStackedBar")
|
||||||
private String type;
|
private String type;
|
||||||
|
|
||||||
@Schema(description = "图表标题")
|
@Schema(description = "图表标题(显示在图片内部)")
|
||||||
private String title;
|
private String title;
|
||||||
|
|
||||||
|
@Schema(description = "Word 文档中的一级标题(章节标题),显示在图片上方,可选")
|
||||||
|
private String heading;
|
||||||
|
|
||||||
|
@Schema(description = "Word 文档中的二级标题,显示在图片上方、一级标题之后,可选")
|
||||||
|
private String subHeading;
|
||||||
|
|
||||||
|
@Schema(description = "图表描述/图注文字,显示在图片下方,可选")
|
||||||
|
private String description;
|
||||||
|
|
||||||
@Schema(description = "X 轴名称")
|
@Schema(description = "X 轴名称")
|
||||||
private String xAxisName;
|
private String xAxisName;
|
||||||
|
|
||||||
@ -34,6 +43,12 @@ public class WordChartVo {
|
|||||||
@Schema(description = "系列数据列表")
|
@Schema(description = "系列数据列表")
|
||||||
private List<WordSeriesVo> series = new ArrayList<>();
|
private List<WordSeriesVo> series = new ArrayList<>();
|
||||||
|
|
||||||
|
@Schema(description = "饼图各分片颜色,与 categories 一一对应,支持 #RRGGBB / 颜色名,不传则用默认配色")
|
||||||
|
private List<String> colors = new ArrayList<>();
|
||||||
|
|
||||||
|
@Schema(description = "饼图扇区之间留白宽度(像素),默认 2,用于相邻种类之间留空隙")
|
||||||
|
private Integer pieGap = 2;
|
||||||
|
|
||||||
@Schema(description = "是否显示数值标签")
|
@Schema(description = "是否显示数值标签")
|
||||||
private boolean showValues = true;
|
private boolean showValues = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,4 +21,7 @@ public class WordSeriesVo {
|
|||||||
|
|
||||||
@Schema(description = "Y 轴索引:0=主轴(左), 1=副轴(右),默认 0。折柱混合图用")
|
@Schema(description = "Y 轴索引:0=主轴(左), 1=副轴(右),默认 0。折柱混合图用")
|
||||||
private Integer yAxisIndex = 0;
|
private Integer yAxisIndex = 0;
|
||||||
|
|
||||||
|
@Schema(description = "系列颜色,支持 #RRGGBB / #AARRGGBB / 颜色名(如 red),不传则用默认配色")
|
||||||
|
private String color;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import org.jfree.chart.plot.DatasetRenderingOrder;
|
|||||||
import org.jfree.chart.plot.PiePlot;
|
import org.jfree.chart.plot.PiePlot;
|
||||||
import org.jfree.chart.plot.PlotOrientation;
|
import org.jfree.chart.plot.PlotOrientation;
|
||||||
import org.jfree.chart.renderer.category.BarRenderer;
|
import org.jfree.chart.renderer.category.BarRenderer;
|
||||||
|
import org.jfree.chart.renderer.category.CategoryItemRenderer;
|
||||||
import org.jfree.chart.renderer.category.LineAndShapeRenderer;
|
import org.jfree.chart.renderer.category.LineAndShapeRenderer;
|
||||||
import org.jfree.chart.renderer.category.StackedBarRenderer;
|
import org.jfree.chart.renderer.category.StackedBarRenderer;
|
||||||
import org.jfree.chart.ui.RectangleInsets;
|
import org.jfree.chart.ui.RectangleInsets;
|
||||||
@ -25,6 +26,7 @@ import java.awt.BasicStroke;
|
|||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -108,6 +110,20 @@ public final class ChartRenderUtil {
|
|||||||
plot.setLabelBackgroundPaint(new Color(255, 255, 255, 200));
|
plot.setLabelBackgroundPaint(new Color(255, 255, 255, 200));
|
||||||
plot.setLabelOutlinePaint(null);
|
plot.setLabelOutlinePaint(null);
|
||||||
plot.setLabelShadowPaint(null);
|
plot.setLabelShadowPaint(null);
|
||||||
|
|
||||||
|
// 自定义分片颜色
|
||||||
|
List<String> colors = chart.getColors();
|
||||||
|
for (int i = 0; i < categories.size(); i++) {
|
||||||
|
Color color = parseColor(i < colors.size() ? colors.get(i) : null);
|
||||||
|
if (color != null) {
|
||||||
|
plot.setSectionPaint(categories.get(i), color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 相邻分片之间留白:白色描边形成空隙
|
||||||
|
float gap = chart.getPieGap() == null ? 2f : chart.getPieGap().floatValue();
|
||||||
|
plot.setSectionOutlinesVisible(true);
|
||||||
|
plot.setDefaultSectionOutlinePaint(Color.WHITE);
|
||||||
|
plot.setDefaultSectionOutlineStroke(new BasicStroke(Math.max(gap, 0f)));
|
||||||
return jfreeChart;
|
return jfreeChart;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,6 +139,7 @@ public final class ChartRenderUtil {
|
|||||||
CategoryPlot plot = jfreeChart.getCategoryPlot();
|
CategoryPlot plot = jfreeChart.getCategoryPlot();
|
||||||
stylePlot(plot);
|
stylePlot(plot);
|
||||||
BarRenderer renderer = (BarRenderer) plot.getRenderer();
|
BarRenderer renderer = (BarRenderer) plot.getRenderer();
|
||||||
|
applySeriesPaints(renderer, chart.getSeries());
|
||||||
renderer.setDefaultItemLabelsVisible(chart.isShowValues());
|
renderer.setDefaultItemLabelsVisible(chart.isShowValues());
|
||||||
renderer.setDefaultItemLabelGenerator(
|
renderer.setDefaultItemLabelGenerator(
|
||||||
new StandardCategoryItemLabelGenerator("{2}", new DecimalFormat("0.##")));
|
new StandardCategoryItemLabelGenerator("{2}", new DecimalFormat("0.##")));
|
||||||
@ -143,6 +160,7 @@ public final class ChartRenderUtil {
|
|||||||
CategoryPlot plot = jfreeChart.getCategoryPlot();
|
CategoryPlot plot = jfreeChart.getCategoryPlot();
|
||||||
stylePlot(plot);
|
stylePlot(plot);
|
||||||
LineAndShapeRenderer renderer = (LineAndShapeRenderer) plot.getRenderer();
|
LineAndShapeRenderer renderer = (LineAndShapeRenderer) plot.getRenderer();
|
||||||
|
applySeriesPaints(renderer, chart.getSeries());
|
||||||
renderer.setDefaultShapesVisible(true);
|
renderer.setDefaultShapesVisible(true);
|
||||||
renderer.setDefaultShapesFilled(true);
|
renderer.setDefaultShapesFilled(true);
|
||||||
renderer.setDefaultStroke(new BasicStroke(2.0f));
|
renderer.setDefaultStroke(new BasicStroke(2.0f));
|
||||||
@ -168,6 +186,7 @@ public final class ChartRenderUtil {
|
|||||||
CategoryPlot plot = jfreeChart.getCategoryPlot();
|
CategoryPlot plot = jfreeChart.getCategoryPlot();
|
||||||
stylePlot(plot);
|
stylePlot(plot);
|
||||||
StackedBarRenderer renderer = (StackedBarRenderer) plot.getRenderer();
|
StackedBarRenderer renderer = (StackedBarRenderer) plot.getRenderer();
|
||||||
|
applySeriesPaints(renderer, chart.getSeries());
|
||||||
renderer.setDefaultItemLabelsVisible(chart.isShowValues());
|
renderer.setDefaultItemLabelsVisible(chart.isShowValues());
|
||||||
if (percent) {
|
if (percent) {
|
||||||
renderer.setDefaultItemLabelGenerator(
|
renderer.setDefaultItemLabelGenerator(
|
||||||
@ -232,6 +251,28 @@ public final class ChartRenderUtil {
|
|||||||
new StandardCategoryItemLabelGenerator("{2}", new DecimalFormat("0.##")));
|
new StandardCategoryItemLabelGenerator("{2}", new DecimalFormat("0.##")));
|
||||||
lineRenderer.setDefaultItemLabelFont(new Font(FONT_NAME, Font.PLAIN, 10));
|
lineRenderer.setDefaultItemLabelFont(new Font(FONT_NAME, Font.PLAIN, 10));
|
||||||
|
|
||||||
|
// 混合图:柱/线系列各自独立编号,分别应用自定义颜色
|
||||||
|
int barIdx = 0;
|
||||||
|
int lineIdx = 0;
|
||||||
|
for (WordSeriesVo series : chart.getSeries()) {
|
||||||
|
if (series == null || series.getName() == null || series.getValues() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
boolean onRight = series.getYAxisIndex() != null && series.getYAxisIndex() == 1;
|
||||||
|
Color color = parseColor(series.getColor());
|
||||||
|
if (onRight) {
|
||||||
|
if (color != null) {
|
||||||
|
lineRenderer.setSeriesPaint(lineIdx, color);
|
||||||
|
}
|
||||||
|
lineIdx++;
|
||||||
|
} else {
|
||||||
|
if (color != null) {
|
||||||
|
barRenderer.setSeriesPaint(barIdx, color);
|
||||||
|
}
|
||||||
|
barIdx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CategoryPlot plot = new CategoryPlot(barDataset, domainAxis, leftAxis, barRenderer);
|
CategoryPlot plot = new CategoryPlot(barDataset, domainAxis, leftAxis, barRenderer);
|
||||||
plot.setDataset(1, lineDataset);
|
plot.setDataset(1, lineDataset);
|
||||||
plot.setRenderer(1, lineRenderer);
|
plot.setRenderer(1, lineRenderer);
|
||||||
@ -305,4 +346,54 @@ public final class ChartRenderUtil {
|
|||||||
NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
|
NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis();
|
||||||
rangeAxis.setTickLabelFont(new Font(FONT_NAME, Font.PLAIN, 12));
|
rangeAxis.setTickLabelFont(new Font(FONT_NAME, Font.PLAIN, 12));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按系列顺序给渲染器应用自定义颜色(跳过无效系列)
|
||||||
|
*/
|
||||||
|
private static void applySeriesPaints(CategoryItemRenderer renderer, List<WordSeriesVo> seriesList) {
|
||||||
|
if (renderer == null || seriesList == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int idx = 0;
|
||||||
|
for (WordSeriesVo series : seriesList) {
|
||||||
|
if (series == null || series.getName() == null || series.getValues() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Color color = parseColor(series.getColor());
|
||||||
|
if (color != null) {
|
||||||
|
renderer.setSeriesPaint(idx, color);
|
||||||
|
}
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析颜色:支持 #RRGGBB / #AARRGGBB / 0xRRGGBB / Java 颜色名(如 red)
|
||||||
|
*/
|
||||||
|
private static Color parseColor(String color) {
|
||||||
|
if (color == null || color.isBlank()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String c = color.trim();
|
||||||
|
try {
|
||||||
|
if (c.startsWith("#")) {
|
||||||
|
String hex = c.substring(1);
|
||||||
|
if (hex.length() == 6) {
|
||||||
|
return new Color(Integer.parseInt(hex, 16));
|
||||||
|
} else if (hex.length() == 8) {
|
||||||
|
return new Color(Integer.parseInt(hex.substring(2), 16), true);
|
||||||
|
}
|
||||||
|
} else if (c.startsWith("0x") || c.startsWith("0X")) {
|
||||||
|
return new Color(Integer.parseInt(c.substring(2), 16));
|
||||||
|
} else {
|
||||||
|
Field field = Color.class.getField(c.toUpperCase());
|
||||||
|
if (field.getType() == Color.class) {
|
||||||
|
return (Color) field.get(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
// 解析失败则返回 null,使用默认配色
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -78,6 +78,12 @@ public final class WordChartExportUtil {
|
|||||||
byte[] png = toPngBytes(image);
|
byte[] png = toPngBytes(image);
|
||||||
double imageHeightPt = IMAGE_WIDTH_PT * image.getHeight() / image.getWidth();
|
double imageHeightPt = IMAGE_WIDTH_PT * image.getHeight() / image.getWidth();
|
||||||
|
|
||||||
|
// 一级标题
|
||||||
|
appendHeading(document, chart.getHeading(), 16, true);
|
||||||
|
// 二级标题
|
||||||
|
appendHeading(document, chart.getSubHeading(), 13, true);
|
||||||
|
|
||||||
|
// 图片
|
||||||
XWPFParagraph paragraph = document.createParagraph();
|
XWPFParagraph paragraph = document.createParagraph();
|
||||||
paragraph.setAlignment(ParagraphAlignment.CENTER);
|
paragraph.setAlignment(ParagraphAlignment.CENTER);
|
||||||
XWPFRun run = paragraph.createRun();
|
XWPFRun run = paragraph.createRun();
|
||||||
@ -89,11 +95,44 @@ public final class WordChartExportUtil {
|
|||||||
throw new IOException("插入图片失败", e);
|
throw new IOException("插入图片失败", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 图表描述/图注
|
||||||
|
appendDescription(document, chart.getDescription());
|
||||||
|
|
||||||
// 每个图表后空一行,避免图片粘连
|
// 每个图表后空一行,避免图片粘连
|
||||||
XWPFParagraph spacer = document.createParagraph();
|
XWPFParagraph spacer = document.createParagraph();
|
||||||
spacer.setSpacingAfter(120);
|
spacer.setSpacingAfter(120);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void appendHeading(XWPFDocument document, String text, int fontSize, boolean bold) {
|
||||||
|
if (text == null || text.isBlank()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
XWPFParagraph paragraph = document.createParagraph();
|
||||||
|
paragraph.setAlignment(ParagraphAlignment.LEFT);
|
||||||
|
paragraph.setSpacingBefore(160);
|
||||||
|
paragraph.setSpacingAfter(80);
|
||||||
|
XWPFRun run = paragraph.createRun();
|
||||||
|
run.setText(text);
|
||||||
|
run.setBold(bold);
|
||||||
|
run.setFontSize(fontSize);
|
||||||
|
run.setFontFamily("SimSun");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void appendDescription(XWPFDocument document, String text) {
|
||||||
|
if (text == null || text.isBlank()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
XWPFParagraph paragraph = document.createParagraph();
|
||||||
|
paragraph.setAlignment(ParagraphAlignment.CENTER);
|
||||||
|
paragraph.setSpacingBefore(60);
|
||||||
|
XWPFRun run = paragraph.createRun();
|
||||||
|
run.setText(text);
|
||||||
|
run.setFontSize(11);
|
||||||
|
run.setColor("808080");
|
||||||
|
run.setFontFamily("SimSun");
|
||||||
|
}
|
||||||
|
|
||||||
private static byte[] toPngBytes(BufferedImage image) throws IOException {
|
private static byte[] toPngBytes(BufferedImage image) throws IOException {
|
||||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||||
ImageIO.write(image, "png", out);
|
ImageIO.write(image, "png", out);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user