第一次尝试:用awt 包将HTML源码转换为图片
优点:不依赖任何外部JAR包,缺点:对CSS的支持比较差,复杂点的样式就无法展示,且不支持外部引入的CSS和写在style中的CSS,只能写在标签上
Eg:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.ArrayList;
import java.util.UUID;
import javax.swing.JTextPane;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.basic.BasicEditorPaneUI;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
public class GraphUtils {
private final static Log log = LogFactory.getLog(GraphUtils.class);
public static int DEFAULT_IMAGE_WIDTH = 1000;
//默认值最好设置大点,因为我们再导之前,不知道这个流有多大,如果过小,则生成的图片后面的为黑色,因为流没有读取完整
public static int DEFAULT_IMAGE_HEIGHT = 200;
public static boolean paintPage(Graphics g, int hPage, int pageIndex, JTextPane panel) {
Graphics2D g2 = (Graphics2D) g;
Dimension d = ((BasicEditorPaneUI) panel.getUI()).getPreferredSize(panel);
double panelHeight = d.height;
double pageHeight = hPage;
int totalNumPages = (int) Math.ceil(panelHeight / pageHeight);
g2.translate(0f, -(pageIndex - 1) * pageHeight);
panel.paint(g2);
boolean ret = true;
if (pageIndex >= totalNumPages) {
ret = false;
return ret;
}
return ret;
}
/**
* 将BufferedImage转换为图片的信息
*/
public static String toJpeg(BufferedImage image) {
// 获取图片文件的在服务器的路径
String imageName = "G:\\" + UUID.randomUUID().toString() + ".jpg";
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(baos);
JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(image);
param.setQuality(1.0f, false);
encoder.setJPEGEncodeParam(param);
encoder.encode(image);
byte[] buff = baos.toByteArray();
baos.close();
// 将字节流写入文件保存为图片
FileUtils.writeByteArrayToFile(new File(imageName), buff);
} catch (Exception ex) {
log.error("保存删除图片失败:" + ex.getMessage());
}
return imageName;
}
/**
* html转换为jpeg文件
* @param bgColor 图片的背景色
* @param html html的文本信息
* @param width 显示图片的Text容器的宽度
* @param height 显示图片的Text容器的高度
* @param eb 設置容器的边框
* @return
* @throws Exception
*/
private static ArrayList<String> html2jpeg(Color bgColor, String html, int width, int height, EmptyBorder eb)
throws Exception {
ArrayList<String> ret = new ArrayList<String>();
try {
JTextPane tp = new JTextPane();
tp.setSize(width, height);
if (eb == null) {
eb = new EmptyBorder(0, 50, 0, 50);
}
if (bgColor != null) {
tp.setBackground(bgColor);
}
if (width <= 0) {
width = DEFAULT_IMAGE_WIDTH;
}
if (height <= 0) {
height = DEFAULT_IMAGE_HEIGHT;
}
tp.setBorder(eb);
tp.setContentType("text/html");
tp.setText(html);
int pageIndex = 1;
boolean bcontinue = true;
while (bcontinue) {
BufferedImage image = new java.awt.image.BufferedImage(width, height,
java.awt.image.BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setClip(0, 0, width, height);
bcontinue = paintPage(g, height, pageIndex, tp);
g.dispose();
String path = toJpeg(image);
ret.add(path);
pageIndex++;
}
} catch (Exception ex) {
throw ex;
}
return ret;
}
/**
* 将一個html转换为图片
* @param htmls
* @return
* @throws Exception
*/
public static ArrayList<String> toImages(String html) throws Exception {
return html2jpeg(Color.white, html, DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT, new EmptyBorder(0, 0, 0, 0));
}
TestCase:
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = { "classpath*:spring/application.xml", "classpath*:spring/plugin-*.xml" })
public class GraphUtilsTest extends TestCase {
@Autowired
private TaskService taskService;
@Autowired
private VelocitySimpleRelolver velocitySimpleRelolver;
@Test
public void testHtml2Image() {
try {
String imageTemplateFile = "mail/weekly/mile_schedule_task.vm";
//邮件内容
Map<String, Object> velocityData = new HashMap<String, Object>();
String projId = "6a9fb54e3439453e86e819a4d46fdafb";
MileSchedule mileSchedule = taskService.getMileSchedule(projId);
velocityData.put("mileSchedule", mileSchedule);
String htmlstr = velocitySimpleRelolver.relolve(imageTemplateFile, velocityData);
Assert.assertNotNull(htmlstr);
GraphUtils.toImages(htmlstr);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Second: 利用html2image组件
优点:方便,代码简单 缺点:CSS支持极差,比不上Java自身的转化
maven引入:
<dependency>
<groupId>gui.ava</groupId>
<artifactId>html2image</artifactId>
<version>0.9</version>
</dependency>
TestCase:
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = { "classpath*:spring/application.xml", "classpath*:spring/plugin-*.xml" })
public class Html2ImageTest extends TestCase {
@Autowired
private TaskService taskService;
@Autowired
private VelocitySimpleRelolver velocitySimpleRelolver;
@Test
public void testHtml2Image() {
try {
String imageTemplateFile = "mail/weekly/mile_schedule_task.vm";
//邮件内容
Map<String, Object> velocityData = new HashMap<String, Object>();
String projId = "6a9fb54e3439453e86e819a4d46fdafb";
MileSchedule mileSchedule = taskService.getMileSchedule(projId);
velocityData.put("mileSchedule", mileSchedule);
String htmlstr = velocitySimpleRelolver.relolve(imageTemplateFile, velocityData);
System.out.println(htmlstr);
Assert.assertNotNull(htmlstr);
String imageName = "G:/weekly_mile_" + projId + ".png";
HtmlImageGenerator imageGenerator = new HtmlImageGenerator();
imageGenerator.setSize(new Dimension(1000, 200));
imageGenerator.loadHtml(htmlstr);
imageGenerator.getBufferedImage();
imageGenerator.saveAsImage(imageName);
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void testImage() {
HtmlImageGenerator imageGenerator = new HtmlImageGenerator();
imageGenerator.setSize(new Dimension(1000, 200));
imageGenerator.loadUrl("http://www.baidu.com");
imageGenerator.getBufferedImage();
imageGenerator.saveAsImage("G:/aaa.png");
}
}
Third:DJNativeSwing
参考:http://blog.csdn.net/cping1982/article/details/5353049
其中:JAR包下载地址:
http://sourceforge.net/projects/djproject/?source=navbar 此地址下down DJNativeSwing.jar 和DJNativeSwing-SWT.jar
此地址下down org.eclipse.swt 包,选择适合你的开发环境
注意:swt 包针对不同的环境有不同的要求,windows与Linux下的不同,使用maven引入时采用properties方式,将下载的包放入你的maven库,然后在POM中进行maven引入
eg:
<!-- DJNativeSwing -->
<dependency>
<groupId>chrriis.dj.nativeswing</groupId>
<artifactId>DJNativeSwing</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>chrriis.dj.nativeswing.swt</groupId>
<artifactId>DJNativeSwing-SWT</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>${swt.groupId}</groupId>
<artifactId>${swt.artifactId}</artifactId>
<version>4.3</version>
</dependency>
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.Semaphore;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
import chrriis.dj.nativeswing.swtimpl.NativeComponent;
import chrriis.dj.nativeswing.swtimpl.NativeInterface;
import chrriis.dj.nativeswing.swtimpl.components.JWebBrowser;
import chrriis.dj.nativeswing.swtimpl.components.WebBrowserAdapter;
import chrriis.dj.nativeswing.swtimpl.components.WebBrowserEvent;
public class DJNativeSwingUtils extends JPanel {
private static final long serialVersionUID = 8675106494733722832L;
// 行分隔符
final static public String LS = System.getProperty("line.separator", "/n");
// 文件分割符
final static public String FS = System.getProperty("file.separator", "//");
//以javascript脚本获得网页全屏后大小
final static StringBuffer jsDimension;
static {
jsDimension = new StringBuffer();
jsDimension.append("var width = 0;").append(LS);
jsDimension.append("var height = 0;").append(LS);
jsDimension.append("if(document.documentElement) {").append(LS);
jsDimension.append(" width = Math.max(width, document.documentElement.scrollWidth);").append(LS);
jsDimension.append(" height = Math.max(height, document.documentElement.scrollHeight);").append(LS);
jsDimension.append("}").append(LS);
jsDimension.append("if(self.innerWidth) {").append(LS);
jsDimension.append(" width = Math.max(width, self.innerWidth);").append(LS);
jsDimension.append(" height = Math.max(height, self.innerHeight);").append(LS);
jsDimension.append("}").append(LS);
jsDimension.append("if(document.body.scrollWidth) {").append(LS);
jsDimension.append(" width = Math.max(width, document.body.scrollWidth);").append(LS);
jsDimension.append(" height = Math.max(height, document.body.scrollHeight);").append(LS);
jsDimension.append("}").append(LS);
jsDimension.append("return width + ':' + height;");
}
public static JPanel createContent(String htmlStr, final String fileName, final Semaphore semp) {
//浏览器面板
JPanel webBrowserPanel = new JPanel(new BorderLayout());
//swing的内嵌浏览器
final JWebBrowser webBrowser = new JWebBrowser();
//隐藏所有的栏
webBrowser.setBarsVisible(false);
//向浏览器写入html内容
webBrowser.setHTMLContent(htmlStr);
//将浏览器嵌入浏览器面板
webBrowserPanel.add(webBrowser, BorderLayout.CENTER);
//向浏览器增加监听事件
webBrowser.addWebBrowserListener(new WebBrowserAdapter() {
// 监听加载进度
public void loadingProgressChanged(WebBrowserEvent e) {
// 当加载完毕时
if (e.getWebBrowser().getLoadingProgress() == 100) {
try {
//执行JS获取图片的宽度,高度
String result = (String) webBrowser.executeJavascriptWithResult(jsDimension.toString());
int index = result == null ? -1 : result.indexOf(":");
NativeComponent nativeComponent = webBrowser.getNativeComponent();
//获取图片的原始尺寸
Dimension originalSize = nativeComponent.getSize();
//根据JS返回结果设定新的图片尺寸
Dimension imageSize = new Dimension(Integer.parseInt(result.substring(0, index)), Integer
.parseInt(result.substring(index + 1)));
//计算图片的新尺寸
imageSize.width = Math.max(originalSize.width, imageSize.width);
imageSize.height = Math.max(originalSize.height, imageSize.height);
nativeComponent.setSize(imageSize);
//创建一个不带透明色的BufferedImage对象
BufferedImage image = new BufferedImage(imageSize.width, imageSize.height,
BufferedImage.TYPE_INT_RGB);
//对浏览器中图片进行绘色
nativeComponent.paintComponent(image);
//截图
image = image.getSubimage(0, 0, imageSize.width - 25, imageSize.height);
try {
// 输出图像
ImageIO.write(image, "jpg", new File(fileName));
} catch (IOException ex) {
ex.printStackTrace();
}
} finally {
// 退出操作 :释放线程
NativeInterface.close();
semp.release();
}
}
}
});
return webBrowserPanel;
}
}
service调用:
private String createImage(final String htmlStr, final Semaphore semaphore) {
final String fileName = FileUtils.getNewFileName("jpg");
NativeInterface.open();
UIUtils.setPreferredLookAndFeel();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// SWT组件转Swing组件,不初始化父窗体将无法启动webBrowser
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 加载指定页面
frame.getContentPane().add(DJNativeSwingUtils.createContent(htmlStr, fileName, semaphore),
BorderLayout.CENTER);
// 仅初始化,但不显示
frame.invalidate();
frame.pack();
frame.setVisible(false);
}
});
// 在MAC上使用时,因为MAC不支持双进程,仅支持进程内模式,调用此方法进行事件调度。windows和Linux可以忽略此事件调度
//NativeInterface.runEventPump();
return new File(fileName).getAbsolutePath();
}
private int fillPicture(HSSFSheet sheet, Report report, int startRow) throws IOException {
if(null == weekly.getMileSchedule()){
return startRow + 1;
}
// 将高度设置为图片的高度
HSSFRow pictureRow = sheet.getRow(startRow);
pictureRow.setHeightInPoints(162);
String htmlStr = this.createHtml(report);
String imagePath = null;
final Semaphore semp = new Semaphore(1);//设置信号量
try {
semp.acquire();
// 子线程生成图片
imagePath = this.createImage(htmlStr, semp);
// 等待子线程释放信号量
semp.acquire();
} catch (InterruptedException e) {
LOG.error("", e);
} finally {
semp.release();
}
try {
ExcelExportUtils.insertPicture(sheet, startRow, (short) 0, (short) 15, imagePath);
} catch (Exception e) {
e.printStackTrace();
} finally {
// delete 文件
new File(imagePath).delete();
}
return ++startRow;
}
windows 下截图正常,Linux下异常,Linux下必须安装Mollizia内核的浏览器,在服务器上安装了一个Firefox后,也必须安装图形界面,刚开始采用安装虚拟x-server,但是在CentOS 下安装各种依赖,尝试安装图形界面后tomcat也必须在图形界面下启动才能正常截图,后废弃此种方式,采用java原生绘图方式,下一篇博文介绍