java poi生成word,指定word模板,暂不支持任意位置的原生图表
- 1、背景
- 2、技术预研
- 3、pom依赖
- 4、word模板
- 5、转义后的文档
- 6、累活(图表)
- 6.1、有空可以需要花时间自行研究的底层
- 6.2、“狡猾”一下,文档中只存在单一图表,但不在文档末尾
- 6.3、处理空列表(直接清除)
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);
}
}