word poi-tl 图表功能增强,插入图表折线图、柱状图、饼状图

目录

问题

由于在开发功能需求中,word文档需要根据数据动态生成图表,不同的数据类型生成不同的图表信息,而word模版引擎原有功能只能做替换,不满足需求;

解决问题

  • 目前选择的poi-tl的模版引擎,在原有的基础上新增自定义插件来实现功能

poi-tl介绍

poi-tl 是一个基于Apache POI的Word模板引擎,也是一个免费开源的Java类库,你可以非常方便的加入到你的项目中;

Word模板引擎功能 描述
文本 将标签渲染为文本
图片 将标签渲染为图片
表格 将标签渲染为表格
图表 条形图(3D条形图)、柱形图(3D柱形图)、面积图(3D面积图)、折线图(3D折线图)、雷达图、饼图(3D饼图)、散点图等图表渲染
If Condition判断 根据条件隐藏或者显示某些文档内容(包括文本、段落、图片、表格、列表、图表等)
Foreach Loop循环 根据集合循环某些文档内容(包括文本、段落、图片、表格、列表、图表等)
Loop表格行 循环复制渲染表格的某一行
Loop表格列 循环复制渲染表格的某一列
Loop有序列表 支持有序列表的循环,同时支持多级列表
Highlight代码高亮 word中代码块高亮展示,支持26种语言和上百种着色样式
Markdown 将Markdown渲染为word文档
Word批注 完整的批注功能,创建批注、修改批注等
Word附件 Word中插入附件
SDT内容控件 内容控件内标签支持
Textbox文本框 文本框内标签支持
图片替换 将原有图片替换成另一张图片
书签、锚点、超链接 支持设置书签,文档内锚点和超链接功能
Expression Language 完全支持SpringEL表达式,可以扩展更多的表达式:OGNL, MVEL
样式 支持有序列表的循环,同时支持多级列表
模板嵌套 模板包含子模板,子模板再包含子模板
模板嵌套 模板包含子模板,子模板再包含子模板
合并 Word合并Merge,也可以在指定位置进行合并
用户自定义函数(插件) 插件化设计,在文档任何位置执行函数

功能实现

引入依赖

xml 复制代码
		<dependency>
            <groupId>com.deepoove</groupId>
            <artifactId>poi-tl</artifactId>
            <version>1.12.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml-full</artifactId>
            <version>5.2.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.2.5</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>5.2.5</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.25</version>
        </dependency>
        <!-- spring el表达式 -->
        <dependency>
	  		<groupId>org.springframework</groupId>
		  	<artifactId>spring-expression</artifactId>
		 	 <version>5.3.18</version>
		</dependency>

功能介绍

  • 目前支持的图表类型有饼图、柱形图、面积图、折线图、雷达图等
  • 同时支持添加到表格一起渲染

功能实例

饼图

模版
代码
java 复制代码
    @Test
    public void test() throws Exception {
        Configure config = Configure.builder()
                .addPlugin('&',new CustomChartRenderPolicy())
                .useSpringEL(false).build();
        Map<String,Object> dataMap = new HashMap<String, Object>();
        CustomChartSingleSeriesRenderData chartSingleSeriesRenderData = CustomCharts.ofPie("日期", new String[]{"2024-01", "2024-02", "2024-03", "2024-04", "2024-05", "2024-06",
                        "2024-07", "2024-08", "2024-09", "2024-10", "2024-11", "2024-12"})
                .series("数值", new Integer[]{10, 35, 21, 46, 79, 55,
                        39, 32, 71, 28, 22, 11}).setWidthAndHeight(10,10).create();
        dataMap.put("testChars", chartSingleSeriesRenderData);


        ClassPathResource classPathResource = new ClassPathResource("static/word/template.docx");
        try (InputStream resourceInputStream = classPathResource.getInputStream();
             XWPFTemplate template = XWPFTemplate.compile(resourceInputStream,config);){

            template.render(dataMap);
            template.writeAndClose(new FileOutputStream("output.docx"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
效果图

雷达图(模版同饼图)

代码
java 复制代码
 @Test
    public void test() throws Exception {
        Configure config = Configure.builder()
                .addPlugin('&',new CustomChartRenderPolicy())
                .useSpringEL(false).build();
        Map<String,Object> dataMap = new HashMap<String, Object>();
        CustomChartSingleSeriesRenderData chartSingleSeriesRenderData = CustomCharts.ofRadar("日期", new String[]{"2024-01", "2024-02", "2024-03", "2024-04", "2024-05", "2024-06",
                        "2024-07", "2024-08", "2024-09", "2024-10", "2024-11", "2024-12"})
                .series("数值", new Integer[]{10, 35, 21, 46, 79, 55,
                        39, 32, 71, 28, 22, 11}).setWidthAndHeight(10,10).create();
        dataMap.put("testChars", chartSingleSeriesRenderData);


        ClassPathResource classPathResource = new ClassPathResource("static/word/template.docx");
        try (InputStream resourceInputStream = classPathResource.getInputStream();
             XWPFTemplate template = XWPFTemplate.compile(resourceInputStream,config);){

            template.render(dataMap);
            template.writeAndClose(new FileOutputStream("output.docx"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
效果图

柱状图(模版同饼图)

代码
java 复制代码
 @Test
    public void test() throws Exception {
        Configure config = Configure.builder()
                .addPlugin('&',new CustomChartRenderPolicy())
                .useSpringEL(false).build();
        Map<String,Object> dataMap = new HashMap<String, Object>();
        CustomChartSingleSeriesRenderData chartSingleSeriesRenderData = CustomCharts.ofBar("日期", new String[]{"2024-01", "2024-02", "2024-03", "2024-04", "2024-05", "2024-06",
                        "2024-07", "2024-08", "2024-09", "2024-10", "2024-11", "2024-12"})
                .series("数值", new Integer[]{10, 35, 21, 46, 79, 55,
                        39, 32, 71, 28, 22, 11}).setWidthAndHeight(10,10).create();
        dataMap.put("testChars", chartSingleSeriesRenderData);


        ClassPathResource classPathResource = new ClassPathResource("static/word/template.docx");
        try (InputStream resourceInputStream = classPathResource.getInputStream();
             XWPFTemplate template = XWPFTemplate.compile(resourceInputStream,config);){

            template.render(dataMap);
            template.writeAndClose(new FileOutputStream("output.docx"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
效果图

附加

CustomCharts 工具类

java 复制代码
import com.deepoove.poi.data.RenderData;
import com.deepoove.poi.data.RenderDataBuilder;
import com.deepoove.poi.data.SeriesRenderData;
import org.apache.poi.xddf.usermodel.chart.ChartTypes;

public class CustomCharts {

    public static CustomCharts.ChartSingles ofArea(String chartTitle, String[] categories) {
        return ofSingleSeries(chartTitle, categories, ChartTypes.AREA);
    }

    public static CustomCharts.ChartSingles ofRadar(String chartTitle, String[] categories) {
        return ofSingleSeries(chartTitle, categories, ChartTypes.RADAR);
    }

    public static CustomCharts.ChartSingles ofLine(String chartTitle, String[] categories) {
        return ofSingleSeries(chartTitle, categories, ChartTypes.LINE);
    }

    public static CustomCharts.ChartSingles ofBar(String chartTitle, String[] categories) {
        return ofSingleSeries(chartTitle, categories, ChartTypes.BAR);
    }

    public static CustomCharts.ChartSingles ofPie(String chartTitle, String[] categories) {
        return ofSingleSeries(chartTitle, categories, ChartTypes.PIE);
    }

    public static CustomCharts.ChartSingles ofPie3D(String chartTitle, String[] categories) {
        return ofSingleSeries(chartTitle, categories, ChartTypes.PIE3D);
    }

    public static CustomCharts.ChartSingles ofDoughnut(String chartTitle, String[] categories) {
        return ofSingleSeries(chartTitle, categories, ChartTypes.DOUGHNUT);
    }

    public static CustomCharts.ChartSingles ofSingleSeries(String chartTitle, String[] categories, ChartTypes chartTypes) {
        return new CustomCharts.ChartSingles(chartTitle, categories, chartTypes);
    }

    public static interface ChartSetting<T extends RenderData> {
        CustomCharts.ChartBuilder<T> setxAsixTitle(String xAxisTitle);

        CustomCharts.ChartBuilder<T> setyAsixTitle(String yAxisTitle);
    }

    public static abstract class ChartBuilder<T extends RenderData> implements RenderDataBuilder<T>, CustomCharts.ChartSetting<T> {
        protected String chartTitle;
        protected String xAxisTitle;
        protected String yAxisTitle;
        protected String[] categories;
        protected ChartTypes chartTypes;

        protected ChartBuilder(String chartTitle, String[] categories, ChartTypes chartTypes) {
            this.chartTitle = chartTitle;
            this.categories = categories;
            this.chartTypes = chartTypes;
        }

        protected void checkLengh(int length) {
            if (categories.length != length) {
                throw new IllegalArgumentException(
                        "The length of categories and series values in chart must be the same!");
            }
        }

        public CustomCharts.ChartBuilder<T> setxAsixTitle(String xAxisTitle) {
            this.xAxisTitle = xAxisTitle;
            return this;
        }

        public CustomCharts.ChartBuilder<T> setyAsixTitle(String yAxisTitle) {
            this.yAxisTitle = yAxisTitle;
            return this;
        }
    }


    /**
     * builder to build single series chart
     */
    public static class ChartSingles extends CustomCharts.ChartBuilder<CustomChartSingleSeriesRenderData> {
        private SeriesRenderData series;
        /**
         * 宽度
         */
        private Integer width = 10;

        /**
         * 高度
         */
        private Integer height = 6;

        private ChartSingles(String chartTitle, String[] categories, ChartTypes chartTypes) {
            super(chartTitle, categories, chartTypes);
        }

        public CustomCharts.ChartSingles series(String name, Number[] value) {
            checkLengh(value.length);
            series = new SeriesRenderData(name, value);
            return this;
        }

        public CustomCharts.ChartSingles setWidthAndHeight(Integer width, Integer height) {
            this.width = width;
            this.height = height;
            return this;
        }

        @Override
        public CustomChartSingleSeriesRenderData create() {
            CustomChartSingleSeriesRenderData data = new CustomChartSingleSeriesRenderData();
            data.setChartTitle(chartTitle);
            data.setxAxisTitle(xAxisTitle);
            data.setyAxisTitle(yAxisTitle);
            data.setCategories(categories);
            data.setSeriesData(series);
            data.setChartTypes(chartTypes);
            data.setWidth(width);
            data.setHeight(height);
            return data;
        }
    }
}

CustomChartSingleSeriesRenderData 数据对象

java 复制代码
import com.deepoove.poi.data.ChartSingleSeriesRenderData;
import lombok.Data;
import org.apache.poi.xddf.usermodel.chart.ChartTypes;

@Data
public class CustomChartSingleSeriesRenderData extends ChartSingleSeriesRenderData {

    private ChartTypes chartTypes;

    /**
     * 宽度
     */
    private Integer width;

    /**
     * 高度
     */
    private Integer height;
}

CustomChartRenderPolicy 插件类

java 复制代码
import cn.hutool.core.util.StrUtil;
import com.deepoove.poi.data.SeriesRenderData;
import com.deepoove.poi.policy.AbstractRenderPolicy;
import com.deepoove.poi.render.RenderContext;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.util.Units;
import org.apache.poi.xddf.usermodel.PresetColor;
import org.apache.poi.xddf.usermodel.XDDFColor;
import org.apache.poi.xddf.usermodel.XDDFShapeProperties;
import org.apache.poi.xddf.usermodel.XDDFSolidFillProperties;
import org.apache.poi.xddf.usermodel.chart.AxisCrosses;
import org.apache.poi.xddf.usermodel.chart.AxisPosition;
import org.apache.poi.xddf.usermodel.chart.BarDirection;
import org.apache.poi.xddf.usermodel.chart.ChartTypes;
import org.apache.poi.xddf.usermodel.chart.LegendPosition;
import org.apache.poi.xddf.usermodel.chart.MarkerStyle;
import org.apache.poi.xddf.usermodel.chart.RadarStyle;
import org.apache.poi.xddf.usermodel.chart.XDDFBarChartData;
import org.apache.poi.xddf.usermodel.chart.XDDFCategoryAxis;
import org.apache.poi.xddf.usermodel.chart.XDDFCategoryDataSource;
import org.apache.poi.xddf.usermodel.chart.XDDFChartAxis;
import org.apache.poi.xddf.usermodel.chart.XDDFChartData;
import org.apache.poi.xddf.usermodel.chart.XDDFChartLegend;
import org.apache.poi.xddf.usermodel.chart.XDDFDataSource;
import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory;
import org.apache.poi.xddf.usermodel.chart.XDDFLineChartData;
import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource;
import org.apache.poi.xddf.usermodel.chart.XDDFPieChartData;
import org.apache.poi.xddf.usermodel.chart.XDDFRadarChartData;
import org.apache.poi.xddf.usermodel.chart.XDDFValueAxis;
import org.apache.poi.xwpf.usermodel.XWPFChart;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTPieSer;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class CustomChartRenderPolicy extends AbstractRenderPolicy<CustomChartSingleSeriesRenderData> {

    private Boolean titleOverlayCode;

    public CustomChartRenderPolicy() {
        this(false);
    }

    public CustomChartRenderPolicy(Boolean titleOverlayCode) {
        this.titleOverlayCode = titleOverlayCode;
    }

    @Override
    protected void afterRender(RenderContext<CustomChartSingleSeriesRenderData> context) {
        //清空标签 clearParagraph 为true 存在表外的图表渲染不了
        clearPlaceholder(context, false);
    }

    @Override
    public void doRender(RenderContext<CustomChartSingleSeriesRenderData> context) throws Exception {
        XWPFRun run = context.getRun();
        XWPFDocument xwpfDocument = (XWPFDocument)context.getXWPFDocument();
        CustomChartSingleSeriesRenderData singleSeriesRenderData = context.getData();
        if (Objects.isNull(singleSeriesRenderData)) {
            return;
        }

        Integer height = singleSeriesRenderData.getHeight();
        Integer width = singleSeriesRenderData.getWidth();
        //在标签位置创建chart图表对象
        XWPFChart chart = xwpfDocument.createChart(run, width * Units.EMU_PER_CENTIMETER, height * Units.EMU_PER_CENTIMETER);

        SeriesRenderData seriesData = singleSeriesRenderData.getSeriesData();

        //图例是否覆盖标题
        chart.setTitleOverlay(this.titleOverlayCode);
        String[] xAxisData = singleSeriesRenderData.getCategories();

        Number[] yAxisData = seriesData.getValues();
        ChartTypes chartTypes = singleSeriesRenderData.getChartTypes();
        //创建图表对象
        execChartData(chart, chartTypes, singleSeriesRenderData, xAxisData, yAxisData);

        //图表相关设置
        //图表标题
        if (StrUtil.isNotEmpty(singleSeriesRenderData.getChartTitle())) {
            chart.setTitleText(singleSeriesRenderData.getChartTitle());
        } else {
            chart.removeTitle();
            chart.deleteLegend();
        }
    }

    private static void solidFillSeries(XDDFChartData.Series series, PresetColor color) {
        XDDFSolidFillProperties fill = new XDDFSolidFillProperties(XDDFColor.from(color));
        XDDFShapeProperties properties = series.getShapeProperties();
        if (properties == null) {
            properties = new XDDFShapeProperties();
        }
        properties.setFillProperties(fill);
        series.setShapeProperties(properties);
    }

    private void execChartData(XWPFChart chart, ChartTypes chartType, CustomChartSingleSeriesRenderData singleSeriesRenderData
            , String[] xAxisData, Number[] yAxisData) {
        XDDFChartData xddfChartData = null;
        switch (chartType) {
            case AREA:

                break;
            case AREA3D:

                break;
            case BAR:
                xddfChartData = performBarRendering(chart, chartType, singleSeriesRenderData, xAxisData, yAxisData);
                break;
            case BAR3D:

                break;
            case DOUGHNUT:
                break;
            case LINE:
                xddfChartData = performLineRendering(chart, chartType, singleSeriesRenderData, xAxisData, yAxisData);
                break;
            case LINE3D:
                break;
            case PIE:
                xddfChartData = performPieRendering(chart, chartType, singleSeriesRenderData, xAxisData, yAxisData);
                break;
            case PIE3D:
                break;
            case RADAR:
                performRadarRendering(chart, chartType, singleSeriesRenderData, xAxisData, yAxisData);
                break;
            case SCATTER:
                break;
            case SURFACE:
                break;
            case SURFACE3D:
                break;
            default:
                break;
        }

        //在标签位置绘制折线图
        if (Objects.nonNull(xddfChartData)) {
            chart.plot(xddfChartData);
        }
    }

    /**
     * PIE
     *
     * @param chart
     * @param chartType
     * @param xAxisData
     * @param yAxisData
     * @return
     */
    private XDDFChartData performPieRendering(XWPFChart chart, ChartTypes chartType, CustomChartSingleSeriesRenderData singleSeriesRenderData
            , String[] xAxisData, Number[] yAxisData) {
        // 图例位置
        XDDFChartLegend legend = chart.getOrAddLegend();
        legend.setPosition(LegendPosition.TOP_RIGHT);
        //设置X轴数据
        XDDFCategoryDataSource xAxisSource = XDDFDataSourcesFactory.fromArray(xAxisData);
        //设置Y轴数据
        XDDFNumericalDataSource<Number> yAxisSource = XDDFDataSourcesFactory.fromArray(yAxisData);
        //创建对象
        // 将数据源绑定到饼图上
        XDDFChartData xddfPieChartData = chart.createData(ChartTypes.PIE, null, null);
        XDDFPieChartData.Series series = (XDDFPieChartData.Series)xddfPieChartData.addSeries(xAxisSource, yAxisSource);
        series.setTitle(null,null);
        // 为了在饼图上显示百分比等信息,需要调用下面的方法
        series.setShowLeaderLines(true);
        if (StrUtil.isEmpty(singleSeriesRenderData.getChartTitle())) {
            // 隐藏图例标识、系列名称、分类名称和数值
            CTPieSer ctPieSer = series.getCTPieSer();
            showCateName(ctPieSer, false);
            showVal(ctPieSer, false);
            showLegendKey(ctPieSer, false);
            showSerName(ctPieSer, false);
        }
        return xddfPieChartData;
    }

    public void showCateName(CTPieSer series, boolean val) {
        if (series.getDLbls().isSetShowCatName()) {
            series.getDLbls().getShowCatName().setVal(val);
        } else {
            series.getDLbls().addNewShowCatName().setVal(val);
        }
    }

    public void showVal(CTPieSer series, boolean val) {
        if (series.getDLbls().isSetShowVal()) {
            series.getDLbls().getShowVal().setVal(val);
        } else {
            series.getDLbls().addNewShowVal().setVal(val);
        }
    }

    public void showSerName(CTPieSer series, boolean val) {
        if (series.getDLbls().isSetShowSerName()) {
            series.getDLbls().getShowSerName().setVal(val);
        } else {
            series.getDLbls().addNewShowSerName().setVal(val);
        }
    }

    public void showLegendKey(CTPieSer series, boolean val) {
        if (series.getDLbls().isSetShowLegendKey()) {
            series.getDLbls().getShowLegendKey().setVal(val);
        } else {
            series.getDLbls().addNewShowLegendKey().setVal(val);
        }
    }


    private XDDFChartData performBarRendering(XWPFChart chart, ChartTypes chartType, CustomChartSingleSeriesRenderData singleSeriesRenderData
            , String[] xAxisData, Number[] yAxisData) {

        // 定义类别轴和数值轴
        XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
        XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
        leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);


        //设置X轴数据
        XDDFCategoryDataSource catSource = XDDFDataSourcesFactory.fromArray(xAxisData);
        //设置Y轴数据
        XDDFNumericalDataSource<Number> valSource = XDDFDataSourcesFactory.fromArray(yAxisData);

        // 创建柱状图数据系列
        XDDFBarChartData barChartData = (XDDFBarChartData) chart.createData(chartType, bottomAxis, leftAxis);
        XDDFBarChartData.Series series1 = (XDDFBarChartData.Series) barChartData.addSeries(catSource, valSource);
        series1.setTitle("示例系列", null); // 设置系列标题

        // 设置柱状图样式
        barChartData.setBarDirection(BarDirection.COL);
        return barChartData;
    }

    private XDDFChartData performRadarRendering(XWPFChart chart, ChartTypes chartType, CustomChartSingleSeriesRenderData singleSeriesRenderData
            , String[] xAxisData, Number[] yAxisData) {
        List<Number[]> list = new ArrayList<>();
        list.add(yAxisData);
        setRadarData(chart, new String[]{"系列一"}, xAxisData, list);
        return null;
    }

    private void setRadarData(XWPFChart chart, String[] series, String[] categories,
                              List<Number[]> list) {
        XDDFChartAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
        XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
        leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
        XDDFRadarChartData radar = (XDDFRadarChartData) chart
                .createData(org.apache.poi.xddf.usermodel.chart.ChartTypes.RADAR, bottomAxis, leftAxis);
        final int numOfPoints = categories.length;
        final String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0));
        final XDDFDataSource<?> categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange, 0);

        for (int i = 0; i < list.size(); i++) {
            final String valuesDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, i + 1, i + 1));
            final XDDFNumericalDataSource<? extends Number> valuesData = XDDFDataSourcesFactory.fromArray(list.get(i),
                    valuesDataRange, i);
            XDDFChartData.Series s = radar.addSeries(categoriesData, valuesData);
            s.setTitle(series[i], chart.setSheetTitle(series[i], i));
        }
        radar.setStyle(RadarStyle.STANDARD);
        chart.plot(radar);
        if (list.size() > 1) {
            XDDFChartLegend legend = chart.getOrAddLegend();
            legend.setPosition(LegendPosition.BOTTOM);
            legend.setOverlay(false);
        }
    }


    /**
     * LINE 渲染
     */
    private XDDFChartData performLineRendering(XWPFChart chart, ChartTypes chartType, CustomChartSingleSeriesRenderData singleSeriesRenderData
            , String[] xAxisData, Number[] yAxisData) {

        //图例设置
        XDDFChartLegend legend = chart.getOrAddLegend();
        //图例位置:上下左右
        legend.setPosition(LegendPosition.TOP);

        //X轴(分类轴)相关设置
        //创建X轴,并且指定位置
        XDDFCategoryAxis xAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);

        String xAxisTitle = singleSeriesRenderData.getxAxisTitle();

        //x轴标题
        if (StrUtil.isNotEmpty(xAxisTitle)) {
            xAxis.setTitle(xAxisTitle);
        }
        //Y轴(值轴)相关设置
        XDDFValueAxis yAxis = chart.createValueAxis(AxisPosition.LEFT); // 创建Y轴,指定位置
        if (StrUtil.isNotEmpty(singleSeriesRenderData.getyAxisTitle())) {
            yAxis.setTitle(singleSeriesRenderData.getyAxisTitle()); // Y轴标题
        }

        //创建折线图对象
        XDDFLineChartData customChartData = (XDDFLineChartData) chart.createData(chartType, xAxis, yAxis);
        //设置X轴数据
        XDDFCategoryDataSource xAxisSource = XDDFDataSourcesFactory.fromArray(xAxisData);
        //设置Y轴数据
        XDDFNumericalDataSource<Number> yAxisSource = XDDFDataSourcesFactory.fromArray(yAxisData);
        //加载折线图数据集
        XDDFLineChartData.Series lineSeries = (XDDFLineChartData.Series) customChartData.addSeries(xAxisSource, yAxisSource);
        //线条样式:true平滑曲线,false折线
        lineSeries.setSmooth(false);
        // 标记点样式
        lineSeries.setMarkerStyle(MarkerStyle.CIRCLE);
        //lineSeries.setMarkerSize((short) 5);

        return customChartData;
    }
}
相关推荐
菜鸟一枚在这35 分钟前
深度解析建造者模式:复杂对象构建的优雅之道
java·开发语言·算法
gyeolhada1 小时前
2025蓝桥杯JAVA编程题练习Day5
java·数据结构·算法·蓝桥杯
菜鸟一枚在这1 小时前
深入理解设计模式之代理模式
java·设计模式·代理模式
小天努力学java1 小时前
【面试系列】Java开发--AI常见面试题
java·人工智能·面试
river661 小时前
java开发——为什么要使用动态代理?
java
Zayn~2 小时前
JVM系列--虚拟机类加载机制
java
m0_748248022 小时前
Redis使用手册
java
CoderCodingNo2 小时前
【GESP】C++二级真题 luogu-b3924, [GESP202312 二级] 小杨的H字矩阵
java·c++·矩阵
2501_903238652 小时前
Spring MVC中环境配置的实战应用
java·spring·mvc·个人开发
程序员侠客行2 小时前
Spring事务原理详解 三
java·后端·spring·架构