上一篇介绍了使用Java的Robot机器人实现截图,然后将剪贴板上的数据流生成PNG图片

但是经过博主的不断测试,在完全依赖远程桌面的没有终端显示器的服务器上

使用截图方式是不可行的,因为一旦使用了远程桌面,再断开桌面,系统后台执行的截图程序会全部截到黑屏

所以博主不得已去用另一种通用方式生成PNG

这就是要使用Apache公司的POI工具,对整个excel进行解析

将excel的每一个单元格,数据,格式,边框全都读取出来

其中需要考虑到单元格的合并,行列的隐藏等等复杂的问题

最终使用JAVA 2d将读取到的数据绘制到PNG图片上。

第一张是Excel原样截图

下面一张是使用JAVA 2D绘制出来的报表:

贴上源代码,其中涉及到使用POI读取单元格背景色,字体前景色等颜色操作,还是比较复杂的。

  1. package com.newflyig.jpg;
  2.  
  3. /**
  4. * width:pix=getColumnWidthInPixels*1.15
  5. * height:pix=getHeightInPoints*96/72
  6. *
  7. * 本示例用来读取Excel报表文件,并力图将报表无差别转化成PNG图片
  8. * 使用POI读取Excel各项数据
  9. * 使用JAVA 2D绘制PNG
  10. * TODO 本示例基本实现了常见Excel的所有样式输出,但Office2007以后的版本添加了条件样式功能,因为POI的API无法读取条件样式,所以无法实现
  11. * 今后可以通过关键字标注等方法,在Excel中需要加入条件样式的单元格用注解标明,使用JAVA计算得出样式再绘制出来
  12. */
  13.  
  14. import java.awt.BasicStroke;
  15. import java.awt.Color;
  16. import java.awt.Font;
  17. import java.awt.FontMetrics;
  18. import java.awt.Graphics2D;
  19. import java.awt.image.BufferedImage;
  20. import java.io.File;
  21. import java.text.DecimalFormat;
  22. import java.util.ArrayList;
  23. import java.util.List;
  24.  
  25. import javax.imageio.ImageIO;
  26.  
  27. import org.apache.poi.hssf.usermodel.HSSFCell;
  28. import org.apache.poi.ss.usermodel.CellStyle;
  29. import org.apache.poi.ss.usermodel.Sheet;
  30. import org.apache.poi.ss.usermodel.Workbook;
  31. import org.apache.poi.ss.usermodel.WorkbookFactory;
  32. import org.apache.poi.ss.util.CellRangeAddress;
  33. import org.apache.poi.xssf.usermodel.XSSFFont;
  34.  
  35. import sun.awt.SunHints;
  36.  
  37. public class DrawFromExcel {
  38.  
  39. public static void main(String[] args) throws Exception {
  40. // 给定两个初始值,标志出导出区域,两个行列组合的单元格
  41. int[] fromIndex = { 0, 0 };
  42. int[] toIndex = { 17, 20 };
  43.  
  44. int imageWidth = 0;
  45. int imageHeight = 0;
  46.  
  47. Workbook wb = WorkbookFactory.create(new File("d:/2014年1月营销活动报表140116.xlsx"));
  48. Sheet sheet = wb.getSheet("test");
  49. List<CellRangeAddress> rangeAddress = sheet.getMergedRegions(); // 获取整个sheet中合并单元格组合的集合
  50.  
  51. // 首先做初步的边界检测,如果指定区域是不合法的则抛出异常
  52. int rowSum = sheet.getPhysicalNumberOfRows();
  53. int colSum = sheet.getRow(0).getPhysicalNumberOfCells();
  54. if (fromIndex[0] > rowSum || fromIndex[0] > toIndex[0] || toIndex[0] > rowSum) {
  55. throw new Exception("the rowIndex of the area is wrong!");
  56. }
  57. if (fromIndex[1] > colSum || fromIndex[1] > toIndex[1] || toIndex[1] > colSum) {
  58. throw new Exception("the colIndex of the area is wrong!");
  59. }
  60.  
  61. // 计算实际需要载入内存的二维Cell数组的大小,剔除隐藏行列
  62. int rowSize = toIndex[0]+1;
  63. int colSize = toIndex[1]+1;
  64.  
  65. // 遍历需要扫描的区域
  66.  
  67. UserCell[][] cells = new UserCell[rowSize][colSize];
  68. int[] rowPixPos = new int[rowSize + 1];
  69. rowPixPos[0] = 0;
  70. int[] colPixPos = new int[colSize + 1];
  71. colPixPos[0] = 0;
  72. for (int i = 0; i < rowSize; i++) {
  73.  
  74. for (int j = 0; j < colSize; j++) {
  75.  
  76. cells[i][j] = new UserCell();
  77. cells[i][j].setCell(sheet.getRow(i).getCell(j));
  78. cells[i][j].setRow(i);
  79. cells[i][j].setCol(j);
  80. boolean ifShow=(i>=fromIndex[0]) && (j>=fromIndex[1]); //首先行列要在指定区域之间
  81. ifShow=ifShow && !(sheet.isColumnHidden(j) || sheet.getRow(i).getZeroHeight()); //其次行列不可以隐藏
  82. cells[i][j].setShow(ifShow);
  83.  
  84. // 计算所求区域宽度
  85. float widthPix = !ifShow ? 0 : sheet.getColumnWidthInPixels(j); // 如果该单元格是隐藏的,则置宽度为0
  86. if (i == fromIndex[0]) {
  87. imageWidth += widthPix;
  88. }
  89.  
  90. colPixPos[j+1] = (int) (widthPix * 1.15 + colPixPos[j]);
  91.  
  92. }
  93.  
  94. // 计算所求区域高度
  95. boolean ifShow=(i>=fromIndex[0]); //行序列在指定区域中间
  96. ifShow=ifShow && !sheet.getRow(i).getZeroHeight(); //行序列不能隐藏
  97. float heightPoint = !ifShow ? 0 : sheet.getRow(i).getHeightInPoints(); // 如果该单元格是隐藏的,则置高度为0
  98. imageHeight += heightPoint;
  99. rowPixPos[i+1] = (int) (heightPoint * 96 / 72) + rowPixPos[i];
  100.  
  101. }
  102.  
  103. imageHeight = imageHeight * 96 / 72;
  104. imageWidth = imageWidth * 115 / 100;
  105.  
  106. wb.close();
  107.  
  108. List<Grid> grids = new ArrayList<Grid>();
  109. for (int i = 0; i < rowSize; i++) {
  110. for (int j = 0; j < colSize; j++) {
  111. Grid grid = new Grid();
  112. // 设置坐标和宽高
  113. grid.setX(colPixPos[j]);
  114. grid.setY(rowPixPos[i]);
  115. grid.setWidth(colPixPos[j + 1] - colPixPos[j]);
  116. grid.setHeight(rowPixPos[i + 1] - rowPixPos[i]);
  117. grid.setRow(cells[i][j].getRow());
  118. grid.setCol(cells[i][j].getCol());
  119. grid.setShow(cells[i][j].isShow());
  120.  
  121. // 判断是否为合并单元格
  122. int[] isInMergedStatus = isInMerged(grid.getRow(), grid.getCol(), rangeAddress);
  123.  
  124. if (isInMergedStatus[0] == 0 && isInMergedStatus[1] == 0) {
  125. // 此单元格是合并单元格,并且不是第一个单元格,需要跳过本次循环,不进行绘制
  126. continue;
  127. } else if (isInMergedStatus[0] != -1 && isInMergedStatus[1] != -1) {
  128. // 此单元格是合并单元格,并且属于第一个单元格,则需要调整网格大小
  129. int lastRowPos=isInMergedStatus[0]>rowSize-1?rowSize-1:isInMergedStatus[0];
  130. int lastColPos=isInMergedStatus[1]>colSize-1?colSize-1:isInMergedStatus[1];
  131.  
  132. grid.setWidth(colPixPos[lastColPos + 1] - colPixPos[j]);
  133. grid.setHeight(rowPixPos[lastRowPos + 1] - rowPixPos[i]);
  134.  
  135. }
  136.  
  137. // 单元格背景颜色
  138. CellStyle cs = cells[i][j].getCell().getCellStyle();
  139. if (cs.getFillPattern() == CellStyle.SOLID_FOREGROUND)
  140. grid.setBgColor(cells[i][j].getCell().getCellStyle().getFillForegroundColorColor());
  141.  
  142. // 设置字体
  143. org.apache.poi.ss.usermodel.Font font = wb.getFontAt(cs.getFontIndex());
  144. grid.setFont(font);
  145.  
  146. // 设置字体前景色
  147. if (font instanceof XSSFFont) {
  148. XSSFFont xf = (XSSFFont) font;
  149.  
  150. grid.setFtColor(xf.getXSSFColor());
  151. }
  152.  
  153. // 设置文本
  154. String strCell = "";
  155. switch (cells[i][j].getCell().getCellType()) {
  156. case HSSFCell.CELL_TYPE_NUMERIC:
  157. strCell = String.valueOf(cells[i][j].getCell().getNumericCellValue());
  158. break;
  159. case HSSFCell.CELL_TYPE_STRING:
  160. strCell = cells[i][j].getCell().getStringCellValue();
  161. break;
  162. case HSSFCell.CELL_TYPE_BOOLEAN:
  163. strCell = String.valueOf(cells[i][j].getCell().getBooleanCellValue());
  164. break;
  165. case HSSFCell.CELL_TYPE_FORMULA:
  166.  
  167. try {
  168. strCell = String.valueOf(cells[i][j].getCell().getNumericCellValue());
  169. } catch (IllegalStateException e) {
  170. strCell = String.valueOf(cells[i][j].getCell().getRichStringCellValue());
  171. }
  172. break;
  173. default:
  174. strCell = "";
  175. }
  176.  
  177. if(cells[i][j].getCell().getCellStyle().getDataFormatString().contains("0.00%")){
  178. try{
  179. double dbCell=Double.valueOf(strCell);
  180. strCell=new DecimalFormat("#.00").format(dbCell*100)+"%";
  181. }catch(NumberFormatException e){}
  182. }
  183.  
  184. grid.setText(strCell.matches("\\w*\\.0") ? strCell.substring(0, strCell.length() - 2) : strCell);
  185.  
  186. grids.add(grid);
  187. }
  188. }
  189.  
  190. BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_RGB);
  191. Graphics2D g2d = image.createGraphics();
  192. // 平滑字体
  193. g2d.setRenderingHint(SunHints.KEY_ANTIALIASING, SunHints.VALUE_ANTIALIAS_OFF);
  194. g2d.setRenderingHint(SunHints.KEY_TEXT_ANTIALIASING, SunHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
  195. g2d.setRenderingHint(SunHints.KEY_STROKE_CONTROL, SunHints.VALUE_STROKE_DEFAULT);
  196. g2d.setRenderingHint(SunHints.KEY_TEXT_ANTIALIAS_LCD_CONTRAST, 140);
  197. g2d.setRenderingHint(SunHints.KEY_FRACTIONALMETRICS, SunHints.VALUE_FRACTIONALMETRICS_OFF);
  198. g2d.setRenderingHint(SunHints.KEY_RENDERING, SunHints.VALUE_RENDER_DEFAULT);
  199.  
  200. g2d.setColor(Color.white);
  201. g2d.fillRect(0, 0, imageWidth, imageHeight);
  202.  
  203. // 绘制表格
  204. for (Grid g : grids) {
  205. if (!g.isShow())
  206. continue;
  207.  
  208. // 绘制背景色
  209. g2d.setColor(g.getBgColor() == null ? Color.white : g.getBgColor());
  210. g2d.fillRect(g.getX(), g.getY(), g.getWidth(), g.getHeight());
  211.  
  212. // 绘制边框
  213. g2d.setColor(Color.black);
  214. g2d.setStroke(new BasicStroke(1));
  215. g2d.drawRect(g.getX(), g.getY(), g.getWidth(), g.getHeight());
  216.  
  217. // 绘制文字,居中显示
  218. g2d.setColor(g.getFtColor());
  219. Font font = g.getFont();
  220. FontMetrics fm = g2d.getFontMetrics(font);
  221. int strWidth = fm.stringWidth(g.getText());// 获取将要绘制的文字宽度
  222. g2d.setFont(font);
  223. g2d.drawString(g.getText(),
  224. g.getX() + (g.getWidth() - strWidth) / 2,
  225. g.getY() + (g.getHeight() - font.getSize()) / 2 + font.getSize());
  226. }
  227.  
  228. g2d.dispose();
  229. ImageIO.write(image, "png", new File("d:/test.png"));
  230.  
  231. System.out.println("Output to PNG file Success!");
  232. }
  233.  
  234. /**
  235. * 判断Excel中的单元格是否为合并单元格
  236. *
  237. * @param row
  238. * @param col
  239. * @param rangeAddress
  240. * @return 如果不是合并单元格返回{-1,-1},如果是合并单元格并且是一个单元格返回{lastRow,lastCol},
  241. * 如果是合并单元格并且不是第一个格子返回{0,0}
  242. */
  243. private static int[] isInMerged(int row, int col, List<CellRangeAddress> rangeAddress) {
  244. int[] isInMergedStatus = { -1, -1 };
  245. for (CellRangeAddress cra : rangeAddress) {
  246. if (row == cra.getFirstRow() && col == cra.getFirstColumn()) {
  247. isInMergedStatus[0] = cra.getLastRow();
  248. isInMergedStatus[1] = cra.getLastColumn();
  249. return isInMergedStatus;
  250. }
  251. if (row >= cra.getFirstRow() && row <= cra.getLastRow()) {
  252. if (col >= cra.getFirstColumn() && col <= cra.getLastColumn()) {
  253. isInMergedStatus[0] = 0;
  254. isInMergedStatus[1] = 0;
  255. return isInMergedStatus;
  256. }
  257. }
  258. }
  259. return isInMergedStatus;
  260. }
  261. }

  

JAVA将Excel中的报表导出为图片格式(三)换一种实现的更多相关文章

  1. JAVA将Excel中的报表导出为图片格式(一)问题背景

    如题所示,先抛出一个问题,如何使用JAVA将Excel中的报表导出为图片格式? 首先说一下这个问题的背景,也就是为什么博主会碰到这个问题 随着微信,易信之流大行其道,企业内部的办公交流.绩效考评甚至考 ...

  2. JAVA将Excel中的报表导出为图片格式(二)实现思路

    接上文,一封类似于下方设计的Excel报表,如何将它指定的区域导出为样式一模一样的JPG图片呢? 要实现这个功能没有现成的解决方案,谷歌度娘了好久也没有,最终自己想了几条思路: 思路1:将报表中的背景 ...

  3. 【转】c# winform DataGridView导出数据到Excel中,可以导出当前页和全部数据

    准备工作就是可以分页的DataGridView,和两个按钮,一个用来导出当前页数据到Excel,一个用来导出全部数据到Excel 没有使用SaveFileDialog,但却可以弹出保存对话框来 先做导 ...

  4. Java处理Excel中的日期格式

    Java处理Excel中的日期格式 2011-12-23 17:34:03|  分类: java |举报 |字号 订阅 下载LOFTER 我的照片书  |   在Excel中的日期格式,其数值为距离1 ...

  5. java 在Excel中插入图片 POI实现

    一.POI简介 Jakarta POI 是apache的子项目,目标是处理ole2对象.它提供了一组操纵Windows文档的Java API 目前比较成熟的是HSSF接口,处理MS Excel(97- ...

  6. java程序转换excel中科学记数法的数据为date类型

    今天出于某些原因从mongodb数据库中导出了一些数据,为了更直观的发送给其他人查阅,便使用mongoVUE的导出为excel功能.   但是导出后出现了一个问题,里边有一列存储时间的,存储的是lon ...

  7. 在纯JaveScript中实现报表导出:从“PDF”到“JPG”

    我们在前端报表中完成了各种工作数据的输入或内容处理之后,需要做什么? 数据的导出! 这些数据的常用导出格式有:PDF.Excel.HTML和图片几大类型. 但总有一些实际应用场景,需要的不仅仅是将现有 ...

  8. Java实现Excel中的NORMSDIST函数和NORMSINV函数

    由于工作中需要将Excel中的此两种函数转换成java函数,从而计算内部评级的资本占用率和资本占用金额.经过多方查阅资料和整理,总结出如下两个转换方法 标准正态分布累计函数NORMSDIST: pub ...

  9. 使用OpenXml把Excel中的数据导出到DataSet中

    public class OpenXmlHelper { /// <summary> /// 读取Excel数据到DataSet中,默认读取所有Sheet中的数据 /// </sum ...

随机推荐

  1. POJ 3164 Command Network 最小树形图

    题目链接: 题目 Command Network Time Limit: 1000MS Memory Limit: 131072K 问题描述 After a long lasting war on w ...

  2. 【POJ】【2068】Nim

    博弈论/DP 这是Nim?这不是巴什博奕的变形吗…… 我也不会捉啊,不过一看最多只有20个人,每人最多拿16个石子,总共只有8196-1个石子,范围好像挺小的,嗯目测暴力可做. so,记忆化搜索直接水 ...

  3. 17.2 The DispatcherServlet

    综述: Spring’s web MVC framework is, like many other web MVC frameworks, request-driven, designed arou ...

  4. Socket 阻塞模式和非阻塞模式

    阻塞I/O模型: 简介:进程会一直阻塞,直到数据拷贝 完成 应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好. 如果数据没有准备好,一直等待….数据准备好了,从内核拷贝到用户空间,IO函数返 ...

  5. Java中super的用法并与this的区别(转载)

    一.子类中如果需要调用父类中的构造函数,则需要使用super(),且必须在构造函数中的第一行 public class Demo1 { public static void main(String[] ...

  6. SPOJ MULTQ3 7299 Multiples of 3 (区间更新)

    题目连接:http://www.spoj.com/problems/MULTQ3/ #include <iostream> #include <stdio.h> #includ ...

  7. struts2-2.3.4.1的struts-default.xml源码

    <?xml version="1.0" encoding="UTF-8" ?> <!-- /* * $Id: struts-default.x ...

  8. Android 设置界面的圆角选项

    请先看一个图: 像这种界面的设计,其实是这样的:整体为一个LinearLayout,一个一个排下去,每一个点击项都是一个RelativeLayout(要为其设置clickable为true),分为左右 ...

  9. Java并发包中常用类小结(一)

    从JDK1.5以后,Java为我们引入了一个并发包,用于解决实际开发中经常用到的并发问题,那我们今天就来简单看一下相关的一些常见类的使用情况. 1.ConcurrentHashMap Concurre ...

  10. 套题T2

    数学(math.cpp) DXY的数学很差... 对于所有1<=i<=N求(2^i – i^2)能被7整除的个数.(N<=1000000) 样例输入: 3 样例输出: 1 你在代码中 ...