上一篇介紹了使用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;
}
}
