JAVA将Excel中的报表导出为图片格式(三)换一种实现
上一篇介绍了使用Java的Robot机器人实现截图,然后将剪贴板上的数据流生成PNG图片
但是经过博主的不断测试,在完全依赖远程桌面的没有终端显示器的服务器上
使用截图方式是不可行的,因为一旦使用了远程桌面,再断开桌面,系统后台执行的截图程序会全部截到黑屏
所以博主不得已去用另一种通用方式生成PNG
这就是要使用Apache公司的POI工具,对整个excel进行解析
将excel的每一个单元格,数据,格式,边框全都读取出来
其中需要考虑到单元格的合并,行列的隐藏等等复杂的问题
最终使用JAVA 2d将读取到的数据绘制到PNG图片上。
第一张是Excel原样截图
下面一张是使用JAVA 2D绘制出来的报表:
贴上源代码,其中涉及到使用POI读取单元格背景色,字体前景色等颜色操作,还是比较复杂的。
- package com.newflyig.jpg;
- /**
- * width:pix=getColumnWidthInPixels*1.15
- * height:pix=getHeightInPoints*96/72
- *
- * 本示例用来读取Excel报表文件,并力图将报表无差别转化成PNG图片
- * 使用POI读取Excel各项数据
- * 使用JAVA 2D绘制PNG
- * TODO 本示例基本实现了常见Excel的所有样式输出,但Office2007以后的版本添加了条件样式功能,因为POI的API无法读取条件样式,所以无法实现
- * 今后可以通过关键字标注等方法,在Excel中需要加入条件样式的单元格用注解标明,使用JAVA计算得出样式再绘制出来
- */
- import java.awt.BasicStroke;
- import java.awt.Color;
- import java.awt.Font;
- import java.awt.FontMetrics;
- import java.awt.Graphics2D;
- import java.awt.image.BufferedImage;
- import java.io.File;
- import java.text.DecimalFormat;
- import java.util.ArrayList;
- import java.util.List;
- import javax.imageio.ImageIO;
- import org.apache.poi.hssf.usermodel.HSSFCell;
- import org.apache.poi.ss.usermodel.CellStyle;
- import org.apache.poi.ss.usermodel.Sheet;
- import org.apache.poi.ss.usermodel.Workbook;
- import org.apache.poi.ss.usermodel.WorkbookFactory;
- import org.apache.poi.ss.util.CellRangeAddress;
- import org.apache.poi.xssf.usermodel.XSSFFont;
- import sun.awt.SunHints;
- public class DrawFromExcel {
- public static void main(String[] args) throws Exception {
- // 给定两个初始值,标志出导出区域,两个行列组合的单元格
- int[] fromIndex = { 0, 0 };
- int[] toIndex = { 17, 20 };
- int imageWidth = 0;
- int imageHeight = 0;
- Workbook wb = WorkbookFactory.create(new File("d:/2014年1月营销活动报表140116.xlsx"));
- Sheet sheet = wb.getSheet("test");
- List<CellRangeAddress> rangeAddress = sheet.getMergedRegions(); // 获取整个sheet中合并单元格组合的集合
- // 首先做初步的边界检测,如果指定区域是不合法的则抛出异常
- int rowSum = sheet.getPhysicalNumberOfRows();
- int colSum = sheet.getRow(0).getPhysicalNumberOfCells();
- if (fromIndex[0] > rowSum || fromIndex[0] > toIndex[0] || toIndex[0] > rowSum) {
- throw new Exception("the rowIndex of the area is wrong!");
- }
- if (fromIndex[1] > colSum || fromIndex[1] > toIndex[1] || toIndex[1] > colSum) {
- throw new Exception("the colIndex of the area is wrong!");
- }
- // 计算实际需要载入内存的二维Cell数组的大小,剔除隐藏行列
- int rowSize = toIndex[0]+1;
- int colSize = toIndex[1]+1;
- // 遍历需要扫描的区域
- UserCell[][] cells = new UserCell[rowSize][colSize];
- int[] rowPixPos = new int[rowSize + 1];
- rowPixPos[0] = 0;
- int[] colPixPos = new int[colSize + 1];
- colPixPos[0] = 0;
- for (int i = 0; i < rowSize; i++) {
- for (int j = 0; j < colSize; j++) {
- cells[i][j] = new UserCell();
- cells[i][j].setCell(sheet.getRow(i).getCell(j));
- cells[i][j].setRow(i);
- cells[i][j].setCol(j);
- boolean ifShow=(i>=fromIndex[0]) && (j>=fromIndex[1]); //首先行列要在指定区域之间
- ifShow=ifShow && !(sheet.isColumnHidden(j) || sheet.getRow(i).getZeroHeight()); //其次行列不可以隐藏
- cells[i][j].setShow(ifShow);
- // 计算所求区域宽度
- float widthPix = !ifShow ? 0 : sheet.getColumnWidthInPixels(j); // 如果该单元格是隐藏的,则置宽度为0
- if (i == fromIndex[0]) {
- imageWidth += widthPix;
- }
- colPixPos[j+1] = (int) (widthPix * 1.15 + colPixPos[j]);
- }
- // 计算所求区域高度
- boolean ifShow=(i>=fromIndex[0]); //行序列在指定区域中间
- ifShow=ifShow && !sheet.getRow(i).getZeroHeight(); //行序列不能隐藏
- float heightPoint = !ifShow ? 0 : sheet.getRow(i).getHeightInPoints(); // 如果该单元格是隐藏的,则置高度为0
- imageHeight += heightPoint;
- rowPixPos[i+1] = (int) (heightPoint * 96 / 72) + rowPixPos[i];
- }
- imageHeight = imageHeight * 96 / 72;
- imageWidth = imageWidth * 115 / 100;
- wb.close();
- List<Grid> grids = new ArrayList<Grid>();
- for (int i = 0; i < rowSize; i++) {
- for (int j = 0; j < colSize; j++) {
- Grid grid = new Grid();
- // 设置坐标和宽高
- grid.setX(colPixPos[j]);
- grid.setY(rowPixPos[i]);
- grid.setWidth(colPixPos[j + 1] - colPixPos[j]);
- grid.setHeight(rowPixPos[i + 1] - rowPixPos[i]);
- grid.setRow(cells[i][j].getRow());
- grid.setCol(cells[i][j].getCol());
- grid.setShow(cells[i][j].isShow());
- // 判断是否为合并单元格
- int[] isInMergedStatus = isInMerged(grid.getRow(), grid.getCol(), rangeAddress);
- if (isInMergedStatus[0] == 0 && isInMergedStatus[1] == 0) {
- // 此单元格是合并单元格,并且不是第一个单元格,需要跳过本次循环,不进行绘制
- continue;
- } else if (isInMergedStatus[0] != -1 && isInMergedStatus[1] != -1) {
- // 此单元格是合并单元格,并且属于第一个单元格,则需要调整网格大小
- int lastRowPos=isInMergedStatus[0]>rowSize-1?rowSize-1:isInMergedStatus[0];
- int lastColPos=isInMergedStatus[1]>colSize-1?colSize-1:isInMergedStatus[1];
- grid.setWidth(colPixPos[lastColPos + 1] - colPixPos[j]);
- grid.setHeight(rowPixPos[lastRowPos + 1] - rowPixPos[i]);
- }
- // 单元格背景颜色
- CellStyle cs = cells[i][j].getCell().getCellStyle();
- if (cs.getFillPattern() == CellStyle.SOLID_FOREGROUND)
- grid.setBgColor(cells[i][j].getCell().getCellStyle().getFillForegroundColorColor());
- // 设置字体
- org.apache.poi.ss.usermodel.Font font = wb.getFontAt(cs.getFontIndex());
- grid.setFont(font);
- // 设置字体前景色
- if (font instanceof XSSFFont) {
- XSSFFont xf = (XSSFFont) font;
- grid.setFtColor(xf.getXSSFColor());
- }
- // 设置文本
- String strCell = "";
- switch (cells[i][j].getCell().getCellType()) {
- case HSSFCell.CELL_TYPE_NUMERIC:
- strCell = String.valueOf(cells[i][j].getCell().getNumericCellValue());
- break;
- case HSSFCell.CELL_TYPE_STRING:
- strCell = cells[i][j].getCell().getStringCellValue();
- break;
- case HSSFCell.CELL_TYPE_BOOLEAN:
- strCell = String.valueOf(cells[i][j].getCell().getBooleanCellValue());
- break;
- case HSSFCell.CELL_TYPE_FORMULA:
- try {
- strCell = String.valueOf(cells[i][j].getCell().getNumericCellValue());
- } catch (IllegalStateException e) {
- strCell = String.valueOf(cells[i][j].getCell().getRichStringCellValue());
- }
- break;
- default:
- strCell = "";
- }
- if(cells[i][j].getCell().getCellStyle().getDataFormatString().contains("0.00%")){
- try{
- double dbCell=Double.valueOf(strCell);
- strCell=new DecimalFormat("#.00").format(dbCell*100)+"%";
- }catch(NumberFormatException e){}
- }
- grid.setText(strCell.matches("\\w*\\.0") ? strCell.substring(0, strCell.length() - 2) : strCell);
- grids.add(grid);
- }
- }
- BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
- Graphics2D g2d = image.createGraphics();
- // 平滑字体
- g2d.setRenderingHint(SunHints.KEY_ANTIALIASING, SunHints.VALUE_ANTIALIAS_OFF);
- g2d.setRenderingHint(SunHints.KEY_TEXT_ANTIALIASING, SunHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
- g2d.setRenderingHint(SunHints.KEY_STROKE_CONTROL, SunHints.VALUE_STROKE_DEFAULT);
- g2d.setRenderingHint(SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST, 140);
- g2d.setRenderingHint(SunHints.KEY_FRACTIONALMETRICS, SunHints.VALUE_FRACTIONALMETRICS_OFF);
- g2d.setRenderingHint(SunHints.KEY_RENDERING, SunHints.VALUE_RENDER_DEFAULT);
- g2d.setColor(Color.white);
- g2d.fillRect(0, 0, imageWidth, imageHeight);
- // 绘制表格
- for (Grid g : grids) {
- if (!g.isShow())
- continue;
- // 绘制背景色
- g2d.setColor(g.getBgColor() == null ? Color.white : g.getBgColor());
- g2d.fillRect(g.getX(), g.getY(), g.getWidth(), g.getHeight());
- // 绘制边框
- g2d.setColor(Color.black);
- g2d.setStroke(new BasicStroke(1));
- g2d.drawRect(g.getX(), g.getY(), g.getWidth(), g.getHeight());
- // 绘制文字,居中显示
- g2d.setColor(g.getFtColor());
- Font font = g.getFont();
- FontMetrics fm = g2d.getFontMetrics(font);
- int strWidth = fm.stringWidth(g.getText());// 获取将要绘制的文字宽度
- g2d.setFont(font);
- g2d.drawString(g.getText(),
- g.getX() + (g.getWidth() - strWidth) / 2,
- g.getY() + (g.getHeight() - font.getSize()) / 2 + font.getSize());
- }
- g2d.dispose();
- ImageIO.write(image, "png", new File("d:/test.png"));
- System.out.println("Output to PNG file Success!");
- }
- /**
- * 判断Excel中的单元格是否为合并单元格
- *
- * @param row
- * @param col
- * @param rangeAddress
- * @return 如果不是合并单元格返回{-1,-1},如果是合并单元格并且是一个单元格返回{lastRow,lastCol},
- * 如果是合并单元格并且不是第一个格子返回{0,0}
- */
- private static int[] isInMerged(int row, int col, List<CellRangeAddress> rangeAddress) {
- int[] isInMergedStatus = { -1, -1 };
- for (CellRangeAddress cra : rangeAddress) {
- if (row == cra.getFirstRow() && col == cra.getFirstColumn()) {
- isInMergedStatus[0] = cra.getLastRow();
- isInMergedStatus[1] = cra.getLastColumn();
- return isInMergedStatus;
- }
- if (row >= cra.getFirstRow() && row <= cra.getLastRow()) {
- if (col >= cra.getFirstColumn() && col <= cra.getLastColumn()) {
- isInMergedStatus[0] = 0;
- isInMergedStatus[1] = 0;
- return isInMergedStatus;
- }
- }
- }
- return isInMergedStatus;
- }
- }
JAVA将Excel中的报表导出为图片格式(三)换一种实现的更多相关文章
- JAVA将Excel中的报表导出为图片格式(一)问题背景
如题所示,先抛出一个问题,如何使用JAVA将Excel中的报表导出为图片格式? 首先说一下这个问题的背景,也就是为什么博主会碰到这个问题 随着微信,易信之流大行其道,企业内部的办公交流.绩效考评甚至考 ...
- JAVA将Excel中的报表导出为图片格式(二)实现思路
接上文,一封类似于下方设计的Excel报表,如何将它指定的区域导出为样式一模一样的JPG图片呢? 要实现这个功能没有现成的解决方案,谷歌度娘了好久也没有,最终自己想了几条思路: 思路1:将报表中的背景 ...
- 【转】c# winform DataGridView导出数据到Excel中,可以导出当前页和全部数据
准备工作就是可以分页的DataGridView,和两个按钮,一个用来导出当前页数据到Excel,一个用来导出全部数据到Excel 没有使用SaveFileDialog,但却可以弹出保存对话框来 先做导 ...
- Java处理Excel中的日期格式
Java处理Excel中的日期格式 2011-12-23 17:34:03| 分类: java |举报 |字号 订阅 下载LOFTER 我的照片书 | 在Excel中的日期格式,其数值为距离1 ...
- java 在Excel中插入图片 POI实现
一.POI简介 Jakarta POI 是apache的子项目,目标是处理ole2对象.它提供了一组操纵Windows文档的Java API 目前比较成熟的是HSSF接口,处理MS Excel(97- ...
- java程序转换excel中科学记数法的数据为date类型
今天出于某些原因从mongodb数据库中导出了一些数据,为了更直观的发送给其他人查阅,便使用mongoVUE的导出为excel功能. 但是导出后出现了一个问题,里边有一列存储时间的,存储的是lon ...
- 在纯JaveScript中实现报表导出:从“PDF”到“JPG”
我们在前端报表中完成了各种工作数据的输入或内容处理之后,需要做什么? 数据的导出! 这些数据的常用导出格式有:PDF.Excel.HTML和图片几大类型. 但总有一些实际应用场景,需要的不仅仅是将现有 ...
- Java实现Excel中的NORMSDIST函数和NORMSINV函数
由于工作中需要将Excel中的此两种函数转换成java函数,从而计算内部评级的资本占用率和资本占用金额.经过多方查阅资料和整理,总结出如下两个转换方法 标准正态分布累计函数NORMSDIST: pub ...
- 使用OpenXml把Excel中的数据导出到DataSet中
public class OpenXmlHelper { /// <summary> /// 读取Excel数据到DataSet中,默认读取所有Sheet中的数据 /// </sum ...
随机推荐
- POJ 3164 Command Network 最小树形图
题目链接: 题目 Command Network Time Limit: 1000MS Memory Limit: 131072K 问题描述 After a long lasting war on w ...
- 【POJ】【2068】Nim
博弈论/DP 这是Nim?这不是巴什博奕的变形吗…… 我也不会捉啊,不过一看最多只有20个人,每人最多拿16个石子,总共只有8196-1个石子,范围好像挺小的,嗯目测暴力可做. so,记忆化搜索直接水 ...
- 17.2 The DispatcherServlet
综述: Spring’s web MVC framework is, like many other web MVC frameworks, request-driven, designed arou ...
- Socket 阻塞模式和非阻塞模式
阻塞I/O模型: 简介:进程会一直阻塞,直到数据拷贝 完成 应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好. 如果数据没有准备好,一直等待….数据准备好了,从内核拷贝到用户空间,IO函数返 ...
- Java中super的用法并与this的区别(转载)
一.子类中如果需要调用父类中的构造函数,则需要使用super(),且必须在构造函数中的第一行 public class Demo1 { public static void main(String[] ...
- SPOJ MULTQ3 7299 Multiples of 3 (区间更新)
题目连接:http://www.spoj.com/problems/MULTQ3/ #include <iostream> #include <stdio.h> #includ ...
- struts2-2.3.4.1的struts-default.xml源码
<?xml version="1.0" encoding="UTF-8" ?> <!-- /* * $Id: struts-default.x ...
- Android 设置界面的圆角选项
请先看一个图: 像这种界面的设计,其实是这样的:整体为一个LinearLayout,一个一个排下去,每一个点击项都是一个RelativeLayout(要为其设置clickable为true),分为左右 ...
- Java并发包中常用类小结(一)
从JDK1.5以后,Java为我们引入了一个并发包,用于解决实际开发中经常用到的并发问题,那我们今天就来简单看一下相关的一些常见类的使用情况. 1.ConcurrentHashMap Concurre ...
- 套题T2
数学(math.cpp) DXY的数学很差... 对于所有1<=i<=N求(2^i – i^2)能被7整除的个数.(N<=1000000) 样例输入: 3 样例输出: 1 你在代码中 ...