java poi word

2 篇文章 0 订阅

1、背景

公司最近在搞报表,业务方要求生成word文档的报表,充斥着大量的图表和文字,好在接口、数据处理逻辑比较清晰。
研发是什么?牲口!我们能说什么?干呗!
在这里插入图片描述

2、技术预研

目前网上两种主流:poi 和 freemark。凭着快速上手和轻便的原则,选了poi。
在这也是感谢各位大佬的技术分享!!!
在这里插入图片描述

3、pom依赖

因为我既存在文档末尾生成原生图表,又存在指定位置生成图表的ImageEntity,所以导的包比较多

<dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-base</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.jfree</groupId>
            <artifactId>jcommon</artifactId>
            <version>1.0.24</version>
        </dependency>
        <dependency>
            <groupId>org.jfree</groupId>
            <artifactId>jfreechart</artifactId>
            <version>1.5.0</version>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi</artifactId>
            <version>4.1.2</version>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-spring-boot-starter</artifactId>
            <version>4.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml-schemas</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-scratchpad</artifactId>
            <version>4.1.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.xmlbeans</groupId>
            <artifactId>xmlbeans</artifactId>
            <version>3.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>ooxml-schemas</artifactId>
            <version>1.4</version>
        </dependency>

4、word模板

1、
在这里插入图片描述
2、
在这里插入图片描述

上图覆盖了几种基本的场景
注意:
1、如果发生变量符{{、}}无法被识别的情况(一般是因为word文档中的编码格式问题),请先在记事本中写好变量符,然后拷贝到word文档中;
2、暂不支持给变量中的文字定制颜色,但可以设置变量符的颜色,里面的文字会继承变量符的颜色;
3、变量不可重名

5、转义后的文档

1、简单文本、列表、图表图片
在这里插入图片描述
2、多图表图片
在这里插入图片描述
3、列表和文档末尾的原生图表
在这里插入图片描述

6、累活(图表)

图表的格式设置,工作量较大,需要不停的调试。。。
需要感谢这位大佬的分享:http://www.blogjava.net/huyi0616/archive/2009/05/26/278046.html, 写的很好,欢迎大家有空去看看

6.1、有空可以需要花时间自行研究的底层
1、模板转义的代码:WordExportUtil.exportWord07
2、JFreeChart, ChartUtilssaveChartAsJPEG(File file, JFreeChart chart, int width, int height)
  (通过Jfreechart生成imageEntity,将原生图表转成图片对象,转义写入文档中的任意指定位置; 但是图片会存在失真的情况,清晰度不够,暂没有好的解决措施)
3、XWPFChart
(文档末尾的原生图表)
6.2、“狡猾”一下,文档中只存在单一图表,但不在文档末尾

1、模板:
在这里插入图片描述
2、代码
在这里插入图片描述
最近有朋友询问关于图例位置的设置问题,我把上图中隐藏掉的代码完整贴出来

// 设置图表折线格式
            CTPlotArea plotArea = chart.getCTChart().getPlotArea();
            for (CTPieSer ser : plotArea.getPieChartList().get(0).getSerList()) {
                CTDLbls ctdLbls = ser.addNewDLbls();
                ctdLbls.addNewShowCatName().setVal(true);// 是否展示对应x轴上的值(类型名称)
                ctdLbls.addNewShowVal().setVal(false);// 是否展示数值
                ctdLbls.addNewShowPercent().setVal(true);// 是否展示百分比

                ctdLbls.addNewNumFmt();
                ctdLbls.getNumFmt().setFormatCode("0.00%"); // 数字格式,
                ctdLbls.getNumFmt().setSourceLinked(false);
                ctdLbls.addNewShowSerName().setVal(false);// 是否展示归属折线名称(系列名称)
                ctdLbls.addNewShowLegendKey().setVal(false);// 是否展示图例(图例项标示)
                ctdLbls.addNewDLblPos().setVal(STDLblPos.BEST_FIT);// 设置数据标签位置
                ctdLbls.addNewShowLeaderLines().setVal(true);// 连接线

                // 定制颜色
                super.customerSerColor(ser, colorMap);

            }

3、效果图
在这里插入图片描述

6.3、处理空列表(直接清除)
protected void clearEmptyTables(XWPFDocument doc) {
        List<XWPFTable> tables = doc.getTables();
        if (CollectionUtils.isEmpty(tables)) {
            return;
        }
        List<XWPFTable> emptyTables = Lists.newArrayList();
        tables.forEach(table -> {
            if (CollectionUtils.isNotEmpty(table.getRows()) && table.getRows().size() < 2) {
                emptyTables.add(table);
            }
        });
        emptyTables.forEach(emptyTable -> emptyTable.removeRow(0));
    }

好了,开始贴代码:
以下代码,网络上基本可见,

@Data
public class LineDataDTO {

    private String name;

    private Long value;
}
 --------------

import cn.afterturn.easypoi.entity.ImageEntity;
import cn.afterturn.easypoi.word.WordExportUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.poi.util.Units;
import org.apache.poi.xddf.usermodel.chart.AxisPosition;
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.XDDFCategoryAxis;
import org.apache.poi.xddf.usermodel.chart.XDDFCategoryDataSource;
import org.apache.poi.xddf.usermodel.chart.XDDFChartLegend;
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.XDDFValueAxis;
import org.apache.poi.xwpf.usermodel.XWPFChart;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtils;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.StandardChartTheme;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.CategoryLabelPositions;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.DateTickUnit;
import org.jfree.chart.axis.DateTickUnitType;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.labels.StandardCategoryItemLabelGenerator;
import org.jfree.chart.labels.StandardPieSectionLabelGenerator;
import org.jfree.chart.labels.StandardXYItemLabelGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PiePlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.RingPlot;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.category.LineAndShapeRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.ui.RectangleInsets;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.general.DefaultPieDataset;
import org.jfree.data.time.SimpleTimePeriod;
import org.jfree.data.time.TimePeriod;
import org.jfree.data.time.TimePeriodValues;
import org.jfree.data.time.TimePeriodValuesCollection;
import org.jfree.data.xy.XYDataset;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTDLbls;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTLineSer;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTPlotArea;
import org.springframework.util.Assert;
import org.springframework.util.ResourceUtils;

import javax.servlet.http.HttpServletResponse;
import java.awt.Color;
import java.awt.Font;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * word生成工具类
 * @Author: rl
 */
@Slf4j
public class WordUtil {

    /**
     * 在文档末尾 绘制折线图
     * @param document      文档对象
     * @param dataMap       折线图数据
     * @param title         标题
     * @param legendFlag    是否子标题
     * @param dataLabelFlag 是否显示数据标签
     */
    public static void drawLineChart(XWPFDocument document, Map<String, List<LineDataDTO>> dataMap,
                                     String title, boolean legendFlag, boolean dataLabelFlag) throws Exception {
        XWPFChart chart = document.createChart(15 * Units.EMU_PER_CENTIMETER, 10 * Units.EMU_PER_CENTIMETER);
        chart.setTitleText(title); // 图表标题
        if (legendFlag) {
            XDDFChartLegend legend = chart.getOrAddLegend();
            legend.setPosition(LegendPosition.BOTTOM);
        }

        // X、Y轴
        XDDFCategoryAxis xAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
        XDDFValueAxis yAxis = chart.createValueAxis(AxisPosition.LEFT);
        // 创建折线图对象,饼状图不需要X,Y轴,只需要数据集即可
        XDDFLineChartData lineChart = (XDDFLineChartData) chart.createData(ChartTypes.LINE, xAxis, yAxis);

        dataMap.forEach((itemTitle, dataList) -> {

            List<String> xValue = new ArrayList<>();
            List<Long> yValue = new ArrayList<>();

            dataList.forEach(data -> {
                xValue.add(data.getName());
                yValue.add(data.getValue());
            });

            // 设置X轴数据
            XDDFCategoryDataSource xAxisSource = XDDFDataSourcesFactory.fromArray(xValue.toArray(new String[0]));
            // 设置Y轴数据
            XDDFNumericalDataSource<Long> yAxisSource = XDDFDataSourcesFactory.fromArray(yValue.toArray(new Long[0]));

            XDDFLineChartData.Series lineSeries = (XDDFLineChartData.Series) lineChart.addSeries(xAxisSource, yAxisSource);
            lineSeries.setTitle(itemTitle, null); // 图例标题
            lineSeries.setSmooth(false); // 线条样式:true平滑曲线,false折线
            lineSeries.setMarkerSize((short) 6); // 标记点大小
            lineSeries.setMarkerStyle(MarkerStyle.CIRCLE); // 标记点样式
        });

        if (!legendFlag) {
            chart.deleteLegend();
        }

        if (dataLabelFlag) {
            // 设置图表折线格式
            CTPlotArea plotArea = chart.getCTChart().getPlotArea();
            for (CTLineSer ser : plotArea.getLineChartArray(0).getSerList()) {
                CTDLbls ctdLbls = ser.addNewDLbls();
                ctdLbls.addNewShowCatName().setVal(false);// 是否展示对应x轴上的值(类型名称)
                ctdLbls.addNewShowVal().setVal(true);// 是否展示数值
                ctdLbls.addNewShowSerName().setVal(false);// 是否展示归属折线名称(系列名称)
                ctdLbls.addNewShowLegendKey().setVal(false);// 是否展示图例(图例项标示)
                // ctdLbls.addNewDLblPos().setVal(STDLblPos.Enum.forString("t"));// 设置数据标签位置
            }
        }
        // 9、绘制折线图
        chart.plot(lineChart);
    }

    /**
     * 在word文档末尾画饼形图
     * @param document 文件对象
     * @throws Exception 异常
     */
    public static void drawChart(XWPFDocument document) throws Exception {
        // 2、创建chart图表对象,抛出异常
        XWPFChart chart = document.createChart(15 * Units.EMU_PER_CENTIMETER, 10 * Units.EMU_PER_CENTIMETER);

        // 3、图表相关设置
        chart.setTitleText("饼形图"); // 图表标题
        chart.setTitleOverlay(false); // 图例是否覆盖标题

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

        // 5、X轴(分类轴)相关设置:饼图中的图例显示
        String[] xAxisData = new String[] {
                "2021-01","2021-02","2021-03","2021-04","2021-05","2021-06",
                "2021-07","2021-08","2021-09","2021-10","2021-11","2021-12",
        };
        XDDFCategoryDataSource xAxisSource = XDDFDataSourcesFactory.fromArray(xAxisData); // 设置分类数据

        // 6、Y轴(值轴)相关设置:饼图中的圆形显示
        Integer[] yAxisData = new Integer[]{
                10, 35, 21, 46, 79, 88,
                39, 102, 71, 28, 99, 57
        };
        XDDFNumericalDataSource<Integer> yAxisSource = XDDFDataSourcesFactory.fromArray(yAxisData); // 设置值数据

        // 7、创建饼图对象,饼状图不需要X,Y轴,只需要数据集即可
        XDDFPieChartData pieChart = (XDDFPieChartData) chart.createData(ChartTypes.PIE, null, null);

        // 8、加载饼图数据集
        XDDFPieChartData.Series pieSeries = (XDDFPieChartData.Series) pieChart.addSeries(xAxisSource, yAxisSource);
        pieSeries.setTitle("test", null); // 系列提示标题

        // 9、绘制饼图
        chart.plot(pieChart);
    }

    /**
     * 美化工厂
     */
    public static void beautifyChartFactory() {
        // 创建主题样式
        StandardChartTheme standardChartTheme = new StandardChartTheme("CN");
        // 设置标题字体
        standardChartTheme.setExtraLargeFont(new Font("宋体", Font.BOLD, 10));
        // 设置图例的字体
        standardChartTheme.setRegularFont(new Font("宋体", Font.PLAIN, 10));
        // 设置轴向的字体
        standardChartTheme.setLargeFont(new Font("宋体", Font.PLAIN, 10));
        // 设置主题样式
        ChartFactory.setChartTheme(standardChartTheme);
    }

    /**
     * 绘制饼状图转成图片对象 imageEntity
     * @param title 标题
     * @param data  数据集
     * @param width 宽度
     * @param height 高度
     * @return 结果对象
     */
    public static ImageEntity createPieChartImage(String title, Map<String, Integer> data,
                                                  int width, int height, String tempImgPath) {
        beautifyChartFactory();

        // 根据jfree生成一个本地饼状图
        DefaultPieDataset pds = new DefaultPieDataset();
        data.forEach(pds::setValue);
        // 图标标题、数据集合、是否显示图例标识、是否显示tooltips、是否支持超链接
        JFreeChart chart = ChartFactory.createPieChart(title, pds, false, false, false);

        // 设置抗锯齿
        chart.setTextAntiAlias(true);
        PiePlot plot = (PiePlot) chart.getPlot();
        plot.setNoDataMessage("暂无数据");
        // 忽略无值的分类
        plot.setIgnoreNullValues(true);
        plot.setBackgroundAlpha(0f);
        // 设置标签阴影颜色
        plot.setShadowPaint(new Color(255, 255, 255));

        // 重新定义LabelGenerator, 设置百分比2位小数
        // DecimalFormat对象,设置小数位
        DecimalFormat df = new DecimalFormat("0.00%");
        NumberFormat nf = NumberFormat.getNumberInstance();
        StandardPieSectionLabelGenerator sps = new StandardPieSectionLabelGenerator("{0}  {2}", nf, df);
        // 设置标签生成器
        plot.setLabelGenerator(sps);
        ImageEntity imageEntity = new ImageEntity();
        try {
            String filePath = ResourceUtils.getURL(ResourceUtils.CLASSPATH_URL_PREFIX).getPath() + tempImgPath;
            File file = ResourceUtils.getFile(filePath);
            ChartUtils.saveChartAsJPEG(file, chart, width, height);
            imageEntity = new ImageEntity(imgToByte(file), width, height);
        } catch (IOException e1) {
            log.error("生成饼状图失败!");
        }
        Assert.notNull(imageEntity.getData(), "生成饼状图对象失败,数据为空!");
        return imageEntity;
    }

    private static ImageEntity writeImg(String name, JFreeChart chart, int width, int height, String tempImgPath) {
        ImageEntity imageEntity = new ImageEntity();
        try {
            log.info("classpath = {}", ResourceUtils.getURL(ResourceUtils.CLASSPATH_URL_PREFIX).getPath());
            // String filePath = ResourceUtils.getURL(ResourceUtils.CLASSPATH_URL_PREFIX).getPath() + tempImgPath;
            String filePath = tempImgPath;
            File file = ResourceUtils.getFile(filePath);
            ChartUtils.saveChartAsJPEG(file, chart, width, height);
            imageEntity = new ImageEntity(imgToByte(file), width, height);
        } catch (IOException e1) {
            log.error("生成【{}】失败:", name, e1);
        }
        // ResourceUtils.getURL(ResourceUtils.CLASSPATH_URL_PREFIX).getPath() + tempImgPath;
        Assert.notNull(imageEntity.getData(), "生成【"+name+"】对象失败,数据为空!");
        return imageEntity;
    }

    public static ImageEntity writeLineImg(Map<String, List<LineDataDTO>> dataMap, String title, String xName,
                                           String yName, String tempImgPath, boolean legend, boolean showLineData) {
        JFreeChart lineChart = drawLineChart(dataMap, title, xName, yName, legend, showLineData);
        return writeImg("折线图", lineChart, 600, 400, tempImgPath);
    }

    // 绘制XY折线图(X轴为时间,Y轴为数值)
    public static ImageEntity writeXYLineImg(Map<String, List<LineDataDTO>> dataMap, String title, String xName,
                                           String yName, String tempImgPath, boolean legend, boolean showLineData,
                                                  Date xMinDate, Date xMaxDate, Double yMinValue, Double yMaxValue) {
        JFreeChart lineChart = drawGrafanaLine(dataMap, title, xName, yName, legend, showLineData, xMinDate, xMaxDate, yMinValue, yMaxValue);
        return writeImg("折线图", lineChart, 600, 400, tempImgPath);
    }

    public static ImageEntity writePieImg(Map<String, Object> dateMap, String title, Map<String, Color> colorMap,
                                          String tempImgPath, int width, int height, Integer fontSize) {
        JFreeChart pieChart = drawPieChart(dateMap, title, colorMap, fontSize);
        return writeImg("饼形图", pieChart, width, height, tempImgPath);
    }

    public static ImageEntity writeBarImg(List<Map<String, Object>> dateList, String title, String xName,
                                          String yName, String tempImgPath) {
        JFreeChart barChart = drawBarChart(dateList, title, xName, yName);
        return writeImg("柱状图", barChart, 500, 300, tempImgPath);
    }

    public static ImageEntity writeRingImg(Map<String, Object> dateMap, String title, Map<String, Color> colorMap,
                                           String tempImgPath, int width, int height, DecimalFormat df, String labelFormat) {
        JFreeChart ringChart = drawRingChart(dateMap, title, colorMap, df, labelFormat);
        return writeImg("环形图", ringChart, width, height, tempImgPath);
    }

    public static JFreeChart drawGrafanaLine(Map<String, List<LineDataDTO>> dateMap, String title,
                                             String xName, String yName, boolean legend, boolean showLineData,
                                             Date xMinDate, Date xMaxDate, Double yMinValue, Double yMaxValue) {

        XYDataset dataset = getXYDataset(dateMap);
        // 美化工厂
        beautifyChartFactory();
        JFreeChart chart = ChartFactory.createTimeSeriesChart(title, // 标题
                xName, // 横轴,X轴标签
                yName, // 纵轴,Y轴的标签
                dataset // dataset
                );

        // 设置抗锯齿,防止字体显示不清楚
        chart.setTextAntiAlias(false);
        if (legend) {
            chart.getLegend().setItemFont(new Font("宋体", Font.BOLD, 15));
        }
        // 子标签不需要可见
        chart.getLegend().setVisible(false);
        // 标题颜色
        // chart.getTitle().setPaint(Color.WHITE);

        chart.setBackgroundPaint(Color.WHITE);
        // 配置字体(解决中文乱码的通用方法)
        Font xfont = new Font("宋体", Font.PLAIN, 15); // X轴
        Font yfont = new Font("宋体", Font.PLAIN, 15); // Y轴
        Font titleFont = new Font("宋体", Font.BOLD, 15); // 图片标题
        XYPlot xyPlot =  chart.getXYPlot();
        xyPlot.getDomainAxis().setLabelFont(xfont);

        DateAxis dateaxis = (DateAxis)chart.getXYPlot().getDomainAxis();
        dateaxis.setRange(xMinDate, xMaxDate);
        dateaxis.setDateFormatOverride(new SimpleDateFormat("MM/dd"));
        dateaxis.setTickUnit(new DateTickUnit(DateTickUnitType.DAY,1));//x轴单位间隔为1天

        // 设置X、Y坐标轴的字体颜色
        /*dateaxis.setTickLabelPaint(Color.WHITE);
        xyPlot.getRangeAxis().setTickLabelPaint(Color.WHITE);*/

        xyPlot.getRangeAxis().setLabelFont(yfont);
        chart.getTitle().setFont(titleFont);
        xyPlot.setBackgroundPaint(Color.WHITE);
        // x轴 // 分类轴网格是否可见
        xyPlot.setDomainGridlinesVisible(false);
        // y轴 // 数据轴网格是否可见
        xyPlot.setRangeGridlinesVisible(true);
        /*// 设置网格竖线颜色
        xyPlot.setDomainGridlinePaint(new Color(199,208,217));
        // X轴网格样式
        xyPlot.setDomainGridlineStroke(new BasicStroke(0.5f));*/
        // 设置网格横线颜色
        xyPlot.setRangeGridlinePaint(Color.LIGHT_GRAY);
        // Y轴网格样式
        // xyPlot.setRangeGridlineStroke(new BasicStroke(0.5f));
        // 设置曲线图与xy轴的距离
        xyPlot.setAxisOffset(new RectangleInsets(0d, 0d, 0d, 0d));
        // 设置面板字体
        Font labelFont = new Font("宋体", Font.PLAIN, 15);
        // 取得Y轴
        NumberAxis rangeAxis = (NumberAxis) xyPlot.getRangeAxis();
        rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
        // rangeAxis.setAutoRangeIncludesZero(true);
        rangeAxis.setUpperMargin(0.20);
        rangeAxis.setLabelAngle(Math.PI / 2.0);//注释此行Y轴小标题 垂直显示

        rangeAxis.setAutoRange(true);
        if (null != yMaxValue) {
            rangeAxis.setUpperBound(yMaxValue);
        }
        if (null != yMinValue) {
            rangeAxis.setLowerBound(yMinValue);
        }


        // 设置X轴坐标上的文字
        dateaxis.setTickLabelFont(labelFont);
        // 设置X轴的标题文字
        dateaxis.setLabelFont(labelFont);

        rangeAxis.setLabelFont(labelFont);
        rangeAxis.setTickLabelFont(labelFont);

        // 设置距离图片左端距离
        dateaxis.setLowerMargin(0.0);
        // 设置距离图片右端距离
        dateaxis.setUpperMargin(0.0);
        // dateaxis.setLabelPaint(Color.WHITE); // x轴标签:白色
        // 获得renderer 注意这里是下嗍造型到 lineAndShapeRenderer!
        XYLineAndShapeRenderer lineAndShapeRenderer = (XYLineAndShapeRenderer) xyPlot.getRenderer();
        // 是否显示折点
        lineAndShapeRenderer.setDefaultShapesVisible(false);
        // 是否显示折线
        lineAndShapeRenderer.setDefaultLinesVisible(true);
        // series 点(即数据点)间有连线可见 显示折点数据
        lineAndShapeRenderer.setDefaultItemLabelGenerator(new StandardXYItemLabelGenerator());
        // 是否显示数据点
        lineAndShapeRenderer.setDefaultItemLabelsVisible(showLineData);
        return chart;
    }

    /**
     * 创建折线图 JFreeChart
     *
     * @param dateMap
     *            map的key:多折线的名称 value为数据点集合 name:count
     * @param title
     *            标题名
     * @param xName
     *            X轴标题
     * @param yName
     *            Y轴标题
     */
    public static JFreeChart drawLineChart(Map<String, List<LineDataDTO>> dateMap, String title,
                                           String xName, String yName, boolean legend, boolean showLineData) {
        DefaultCategoryDataset dataset = getLineDataset(dateMap);
        // 美化工厂
        beautifyChartFactory();
        JFreeChart chart = ChartFactory.createLineChart(title, // 标题
                xName, // categoryAxisLabel (category轴,横轴,X轴标签)
                yName, // valueAxisLabel(value轴,纵轴,Y轴的标签)
                dataset, // dataset
                PlotOrientation.VERTICAL, legend, // legend
                false, // tooltips
                false); // URLs

        // 设置抗锯齿,防止字体显示不清楚
        chart.setTextAntiAlias(false);
        if (legend) {
            chart.getLegend().setItemFont(new Font("宋体", Font.BOLD, 15));
        }

        chart.setBackgroundPaint(Color.WHITE);
        // 配置字体(解决中文乱码的通用方法)
        Font xfont = new Font("宋体", Font.PLAIN, 15); // X轴
        Font yfont = new Font("宋体", Font.PLAIN, 15); // Y轴
        Font titleFont = new Font("宋体", Font.BOLD, 15); // 图片标题
        CategoryPlot categoryPlot = chart.getCategoryPlot();
        categoryPlot.getDomainAxis().setLabelFont(xfont);
        categoryPlot.getRangeAxis().setLabelFont(yfont);
        chart.getTitle().setFont(titleFont);
        categoryPlot.setBackgroundPaint(Color.WHITE);
        // x轴 // 分类轴网格是否可见
        categoryPlot.setDomainGridlinesVisible(false);
        // y轴 //数据轴网格是否可见
        categoryPlot.setRangeGridlinesVisible(true);
        // 设置网格竖线颜色
        categoryPlot.setDomainGridlinePaint(Color.LIGHT_GRAY);
        // 设置网格横线颜色
        categoryPlot.setRangeGridlinePaint(Color.LIGHT_GRAY);
        // 没有数据时显示的文字说明
        categoryPlot.setNoDataMessage("没有数据显示");
        // 设置曲线图与xy轴的距离
        categoryPlot.setAxisOffset(new RectangleInsets(0d, 0d, 0d, 0d));
        // 设置面板字体
        Font labelFont = new Font("宋体", Font.PLAIN, 15);
        // 取得Y轴
        NumberAxis rangeAxis = (NumberAxis) categoryPlot.getRangeAxis();
        rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
        rangeAxis.setAutoRangeIncludesZero(true);
        rangeAxis.setUpperMargin(0.20);
        rangeAxis.setLabelAngle(Math.PI / 2.0);//注释此行Y轴小标题 垂直显示
        // 取得X轴
        CategoryAxis categoryAxis = categoryPlot.getDomainAxis();
        // 设置X轴坐标上的文字
        categoryAxis.setTickLabelFont(labelFont);
        // categoryAxis.
        // 设置X轴的标题文字
        categoryAxis.setLabelFont(labelFont);
        // 横轴上的 Lable 45度倾斜
        categoryAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_45);
        // 设置距离图片左端距离
        categoryAxis.setLowerMargin(0.0);
        // 设置距离图片右端距离
        categoryAxis.setUpperMargin(0.0);
        if (!legend) {
            // 刻度线不显示
            categoryAxis.setAxisLineVisible(false);
            // x轴不显示
            categoryAxis.setVisible(false);
        }
        // 获得renderer 注意这里是下嗍造型到 lineandshaperenderer!!
        LineAndShapeRenderer lineandshaperenderer = (LineAndShapeRenderer) categoryPlot.getRenderer();
        // 是否显示折点
        lineandshaperenderer.setDefaultShapesVisible(false);
        // 是否显示折线
        lineandshaperenderer.setDefaultLinesVisible(true);
        // series 点(即数据点)间有连线可见 显示折点数据
        lineandshaperenderer.setDefaultItemLabelGenerator(new StandardCategoryItemLabelGenerator());
        // 是否显示数据点
        lineandshaperenderer.setDefaultItemLabelsVisible(showLineData);
        return chart;
    }

    /**
     * 创建柱形图
     *
     * @param dateList
     *            map的key:'name'为x轴值 count为y轴值
     * @param title
     *            标题名
     * @param xName
     *            X轴标题
     * @param yName
     *            Y轴标题
     */
    public static JFreeChart drawBarChart(List<Map<String, Object>> dateList, String title, String xName, String yName) {
        CategoryDataset dataset = getBarDataSet(dateList);
        // 美化工厂
        beautifyChartFactory();
        JFreeChart chart = ChartFactory.createBarChart(title, // 图表标题
                xName, // 目录轴的显示标签
                yName, // 数值轴的显示标签
                dataset, // 数据集
                PlotOrientation.VERTICAL, // 图表方向:水平、垂直
                false, // 是否显示图例(对于简单的柱状图必须是false)
                false, // 是否生成工具
                false // 是否生成URL链接
        );

        // 从这里开始
        CategoryPlot plot = chart.getCategoryPlot();// 获取图表区域对象
        plot.setBackgroundPaint(Color.WHITE);
        // 设置网格横线颜色
        plot.setRangeGridlinePaint(Color.LIGHT_GRAY);

        CategoryAxis domainAxis = plot.getDomainAxis(); // 水平底部列表
        domainAxis.setLabelFont(new Font("宋体", Font.BOLD, 10)); // 水平底部标题
        domainAxis.setTickLabelFont(new Font("宋体", Font.BOLD, 10)); // 垂直标题
        // 横轴上的 Lable 45度倾斜
        domainAxis.setCategoryLabelPositions(CategoryLabelPositions.UP_45);
        ValueAxis rangeAxis = plot.getRangeAxis();// 获取柱状
        rangeAxis.setLabelFont(new Font("宋体", Font.BOLD, 10));
        // chart.getLegend().setItemFont(new Font("黑体", Font.BOLD, 15));//不显示图例,此行必须注释
        chart.getTitle().setFont(new Font("宋体", Font.BOLD, 20));// 设置标题字体
        rangeAxis.setUpperMargin(0.1);// 设置顶部Y坐标轴间距,防止数据无法显示
        rangeAxis.setLowerMargin(0.1);// 设置底部Y坐标轴间距

        return chart;
    }

    /**
     * 创建饼图
     *
     * @param dateMap
     *            map的key:'name'为名称 count为值
     * @param colorMap
     *            dateMap的key: 颜色
     * @param title
     *            标题名
     */
    public static JFreeChart drawPieChart(Map<String, Object> dateMap, String title, Map<String, Color> colorMap, Integer fontSize) {
        // 美化工厂
        beautifyChartFactory();

        if (null == fontSize) {
            fontSize = 15;
        }

        DefaultPieDataset data = getPieDataSet(dateMap);
        // 3D 饼形图
        // JFreeChart chart = ChartFactory.createPieChart3D(title, data, false, false, false);
        JFreeChart chart = ChartFactory.createPieChart(title, data, false, false, false);

        // 设置百分比
        PiePlot pieplot = (PiePlot) chart.getPlot();
        DecimalFormat df = new DecimalFormat("0.00%");// 获得一个DecimalFormat对象,主要是设置小数问题
        NumberFormat nf = NumberFormat.getNumberInstance();// 获得一个NumberFormat对象
//		StandardPieSectionLabelGenerator sp1 = new StandardPieSectionLabelGenerator("{0}  {2}", nf, df);// 获得StandardPieSectionLabelGenerator对象
        pieplot.setLabelGenerator(new StandardPieSectionLabelGenerator("{0}:{2}", nf, df));// 设置饼图显示百分比

        // 没有数据的时候显示的内容
        pieplot.setNoDataMessage("无数据显示");
        pieplot.setCircular(false);
        pieplot.setLabelGap(0.02D);

        // 不描绘标签外轮廓
        pieplot.setLabelOutlineStroke(null);
        // 标签字体颜色
        pieplot.setLabelPaint(Color.BLACK);
        // 标签底色
        pieplot.setLabelBackgroundPaint(Color.WHITE);
        // 标签阴影色
        pieplot.setLabelShadowPaint(Color.WHITE);
        // 饼图阴影
        pieplot.setShadowPaint(Color.LIGHT_GRAY);

        pieplot.setIgnoreNullValues(true);// 设置不显示空值
        pieplot.setIgnoreZeroValues(true);// 设置不显示负值
        pieplot.setShadowPaint(new Color(255, 255, 255));
        pieplot.setBackgroundAlpha(0f);
        // 设置外边框不可见
        pieplot.setOutlineVisible(false);

        if (MapUtils.isNotEmpty(colorMap)) {
            colorMap.forEach(pieplot::setSectionPaint);
        }

        chart.getTitle().setFont(new Font("宋体", Font.BOLD, fontSize));// 设置标题字体
        PiePlot piePlot = (PiePlot) chart.getPlot();// 获取图表区域对象
        piePlot.setLabelFont(new Font("宋体", Font.BOLD, fontSize));// 解决乱码
        return chart;
    }

    /**
     * 创建环形图
     *
     * @param dateMap
     *            map的key:'name'为名称 count为值
     * @param title
     *            标题名
     */
    public static JFreeChart drawRingChart(Map<String, Object> dateMap, String title,
                                           Map<String, Color> colorMap, DecimalFormat df, String labelFormat) {
        // 美化工厂
        beautifyChartFactory();

        DefaultPieDataset data = getPieDataSet(dateMap);
        // legend:true,显示子标题
        JFreeChart chart = ChartFactory.createRingChart(title, data, true, false, false);
        // 设置边界线不可见
        chart.setBorderVisible(false);
        chart.getLegend().setItemFont(new Font("宋体", Font.BOLD, 15));

        // 设置百分比
        RingPlot ringplot = (RingPlot) chart.getPlot();
        if (null == df) {
            df = new DecimalFormat("0.00%");// 获得一个DecimalFormat对象,主要是设置小数问题
        }
        NumberFormat nf = NumberFormat.getNumberInstance();// 获得一个NumberFormat对象
//		StandardPieSectionLabelGenerator sp1 = new StandardPieSectionLabelGenerator("{0}  {2}", nf, df);// 获得StandardPieSectionLabelGenerator对象
        // {0}:{1}({2}): xxx: 500(20%)
        if (StringUtils.isEmpty(labelFormat)) {
            labelFormat = "{2}";
        }
        ringplot.setLabelGenerator(new StandardPieSectionLabelGenerator(labelFormat, nf, df));// 设置饼图显示百分比

        // 没有数据的时候显示的内容
        ringplot.setNoDataMessage("无数据显示");
        // 保持圆形
        ringplot.setCircular(true);
        ringplot.setLabelGap(0.02D);

        // 不描绘标签外轮廓
        ringplot.setLabelOutlineStroke(null);
        // 标签字体颜色
        ringplot.setLabelPaint(Color.BLACK);
        // 标签底色
        ringplot.setLabelBackgroundPaint(Color.WHITE);
        // 标签阴影色
        ringplot.setLabelShadowPaint(Color.WHITE);

        ringplot.setIgnoreNullValues(true);// 设置不显示空值
        ringplot.setIgnoreZeroValues(true);// 设置不显示负值
        ringplot.setShadowPaint(new Color(255, 255, 255));
        ringplot.setBackgroundAlpha(0f);
        // 设置外边框不可见
        ringplot.setOutlineVisible(false);

        if (MapUtils.isNotEmpty(colorMap)) {
            colorMap.forEach(ringplot::setSectionPaint);
        }

        // 设置抗锯齿,防止字体显示不清楚
        chart.setTextAntiAlias(false);
        chart.getTitle().setFont(new Font("宋体", Font.BOLD, 15));// 设置标题字体
        chart.getTitle().setPaint(Color.RED);
        ringplot.setLabelFont(new Font("宋体", Font.BOLD, 15));// 解决乱码
        return chart;
    }

    /**
     * 将图片转化为字节数组
     * @return 字节数组
     */
    @SneakyThrows
    private static byte[] imgToByte(File file) {
        byte[] buffer;
        try {
            FileInputStream fis = new FileInputStream(file);
            ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);
            byte[] b = new byte[1000];
            int n;
            while ((n = fis.read(b)) != -1) {
                bos.write(b, 0, n);
            }
            fis.close();
            bos.close();
            buffer = bos.toByteArray();

            // 清空临时文件,
            BufferedWriter bw = new BufferedWriter(new FileWriter(file));
            bw.write("");
            bw.flush();
            bw.close();
            log.info("临时图片文件清空成功");
        } catch (IOException e) {
            log.error("图片转字节异常:", e);
            throw e;
        }
        return buffer;
    }

    // 折线图数据集(支持多折线)
    private static XYDataset getXYDataset(Map<String, List<LineDataDTO>> dateMap) {
        TimePeriodValuesCollection dataset = new TimePeriodValuesCollection();
        dateMap.forEach((key, dataList) -> {
            TimePeriodValues timeSeries = new TimePeriodValues(key);
            for (LineDataDTO lineData : dataList) {
                TimePeriod period = new SimpleTimePeriod(Long.parseLong(lineData.getName())*1000, Long.parseLong(lineData.getName())*1000);
                timeSeries.add(period, lineData.getValue());
            }
            dataset.addSeries(timeSeries);
        });

        return dataset;
    }

    // 折线图数据集(支持多折线)
    private static DefaultCategoryDataset getLineDataset(Map<String, List<LineDataDTO>> dateMap) {
        DefaultCategoryDataset dataset = new DefaultCategoryDataset();
        dateMap.forEach((key, dataList) -> {
            for (LineDataDTO lineData : dataList) {
                dataset.setValue(lineData.getValue(), key, lineData.getName());
            }
        });

        return dataset;
    }

    // 柱状图数据集
    private static CategoryDataset getBarDataSet(List<Map<String, Object>> dateList) {
        DefaultCategoryDataset dataset = new DefaultCategoryDataset();
        for (Map<String, Object> map : dateList) {
            dataset.addValue(Integer.parseInt(map.get("count").toString()), map.get("name").toString(), map.get("name")
                    .toString());
        }
        return dataset;
    }

    // 饼形图数据集
    private static DefaultPieDataset getPieDataSet(Map<String, Object> dataMap) {
        DefaultPieDataset dataset = new DefaultPieDataset();
        dataMap.forEach((name, count) -> dataset.setValue(name, Integer.parseInt(count.toString())));
        return dataset;
    }

    /**
     * 生成word文档
     * @param templatePath  word模板地址
     * @param fileName      文件名
     * @param params        数据集
     * @return              word对象XWPFDocument
     * @throws Exception    something error
     */
    public static XWPFDocument generateWord(String templatePath, String fileName, Map<String, Object> params) throws Exception {
        Assert.notNull(templatePath, "模板路径不能为空");
        Assert.notNull(fileName, "导出文件名不能为空");
        Assert.isTrue(fileName.endsWith(".docx"), "word导出请使用docx格式");
        // 此过程不需要加锁,只是读取模板文件,写到内存中,并没有修改模板文件
        // 不支持对文字的字体、颜色做修改
        log.info("开始转换word模板");
        return WordExportUtil.exportWord07(templatePath, params);
        // 支持对已生成的doc文档再次进行变量转换
        // WordExportUtil.exportWord07(doc, params);
    }
}

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值