根据html来生成图片很多时候都会被用到,最常见的就是网页截图。通过html模板技术,可以让用户自定义一段html,然后为用户实时生成图片。
以下就是使用java 2D实现的html生成图片的方法,需要用到两个开源的jar包。core-renderer.jar,jtidy-r938.jar,这两个jar包是用来渲染html页面的,但是不支持执行javasrcipt。另外还需要xmlgraphics-commons-1.4.jar,这是用来生成PNG图片的。
为了支持中文显示自动换行,需要修改org.xhtmlrenderer.layout.Breaker类。
因为xhtmlrenderer是外国人写的,默认是根据空格来决定是否可以换行的,但是中文语句都是没有空格的,修改后的行为就像样式word-break:break-all。core-renderer-repack.jar是我重新打的包,包含了Breaker.java的源代码。
以下是代码:
public class ImageGenerator {
private float jpgQuality = 0.9F;
private boolean useHighScaleQuality = true;
private ImageFormat imageFormat = ImageFormat.JPG;
private ChineseFontResolver fontResolver;
private static final Logger logger = Logger.getLogger(ImageGenerator.class);
public void init() {
XRLog.init("init log...");
initTidy();
}
private Tidy tidy = null;
private static HashMap
为了使用中文字体,需要手工加载中文字体,这里都是用truetype字体:
/**
* 中文字体支持,默认使用微软雅黑
*
*/
public class ChineseFontResolver extends AWTFontResolver {
private static Font DEFAULT_FONT;
private String fontPath;
public ChineseFontResolver() {
super();
}
public void init() {
try {
InputStream input = new FileInputStream(new File(getFontPath() + "msyh.ttf"));
Font font = Font.createFont(Font.TRUETYPE_FONT, input);
font = font.deriveFont(12.0F);
setFontMapping("微软雅黑", font);
DEFAULT_FONT = font;
setFontMapping("arial", font);
setFontMapping("serif", font);
setFontMapping("SansSerif", font);
setFontMapping("Monospaced", font);
} catch (FontFormatException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
protected Font resolveFont(SharedContext ctx, String font, float size, IdentValue weight, IdentValue style, IdentValue variant) {
Font result = super.resolveFont(ctx, "微软雅黑", size, weight, style, variant);
if (result == null) {
result = DEFAULT_FONT;
}
return result;
}
public void setFontPath(String fontPath) {
this.fontPath = fontPath;
}
public String getFontPath() {
return fontPath;
}
}
truetype的微软雅黑结合ImageGenerator中的RENDER_HINT,渲染出来的中文字体质量还是相当不错的。
使用方法就像这样,为了不出现乱码,最好全部都是用UTF-8编码:
ByteArrayOutputStream output = null;
try {
Map data = new HashMap();
String html = getTemplateParser().parse("template.ftl", data);//使用freemarker填充数据
InputStream input = new ByteArrayInputStream(html.getBytes(Charset.forName("UTF-8")));
output = getImageGenerator().html2Image(input, 1260, 64);
//for test
// FileOutputStream file = new FileOutputStream(new File("d://images//result.jpg"));
// file.write(output.toByteArray());
// file.flush();
// file.close();
} catch (TemplateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
以上代码是使用模板来生成图片,网站截图只要稍微修改一下代码即可,以下是个简单的例子:
public class Main {
public static void main(String[] args) {
try {
Tidy tidy = new Tidy();
tidy.setQuiet(true);
tidy.setXHTML(true);
tidy.setHideComments(true);
tidy.setInputEncoding("UTF-8");
tidy.setOutputEncoding("UTF-8");
tidy.setShowErrors(0);
tidy.setShowWarnings(false);
//for test start
FileOutputStream output = new FileOutputStream(new File("d:/1234.png"));
URL url=new URL("http://www.baidu.com");
URLConnection conn=url.openConnection();
conn.connect();
InputStream is=conn.getInputStream();
BufferedReader br=new BufferedReader(new InputStreamReader(is,"gbk"));
String s = "";
StringBuffer sb = new StringBuffer("");
while ((s = br.readLine()) != null) {
sb.append(s + "\r\n");
}
ByteArrayInputStream bis = new ByteArrayInputStream(sb.toString().getBytes("UTF-8"));
//for test end
Document doc = tidy.parseDOM(bis, null);
Graphics2DRenderer g2r = new Graphics2DRenderer();
g2r.setDocument(doc,"");
//这里其实还是使用固定的width、height去渲染网页,但是可以设置一个较大的值
Dimension dim = new Dimension(1024, 2000);
BufferedImage buff = new BufferedImage((int)dim.getWidth(), (int)dim.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D)buff.getGraphics();
g2r.layout(g, new Dimension(1024, 2000));
g.dispose();
//这里获取真正的网页大小,如果页面有背景图设了repeat,会导致无法获取真实大小
Rectangle rect = g2r.getMinimumSize();
buff = new BufferedImage((int)rect.getWidth(), (int)rect.getHeight(), BufferedImage.TYPE_INT_RGB);
g = (Graphics2D)buff.getGraphics();
g2r.render(g);
g.dispose();
PNGEncodeParam param = PNGEncodeParam.RGB.getDefaultEncodeParam(buff);
PNGImageEncoder imageEncoder = new PNGImageEncoder(output, param);
imageEncoder.encode(buff);
output.flush();
output.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}