前段时间实现了长图文生成的基本功能,然后想了下能否有个进阶版,直接将markdown生成渲染后的图片呢?
有不少的库可以将 markdown 转为 html,那么这个需求就可以转为 html转Image了
可以参看之前的博文《Java 实现 markdown转Html》
主要的核心问题就在这里了,如何实现html转图片?
- 直接实现html转图片的包没怎么见,看到一个
html2image
, 还不太好用 - 在 AWT or Swing 的Panel上显示网页,在把Panel输出为 image 文件
- 使用js相关技术实现转换
本篇博文具体实现以 html2image
的实现逻辑作为参考,然后定制实现一把(后面有机会写一篇利用js来实现html转图片的博文)
html2image
基本上没啥维护了,内部主要是利用了 xhtmlrender
实现html渲染为图片
Graphics2DRenderer renderer = new Graphics2DRenderer();
// 设置渲染内容
renderer.setDocument(document, document.getDocumentURI());
// 获取Graphics2D
graphics2D = bufferedImage.createGraphics();
renderer.layout(graphics2D, dimension);
// 内容渲染
renderer.render(graphics2D);
- 为什么并不直接使用
java-html2image
?
- 因为有些定制的场景支持得不太友好,加上源码也比较简单,所以干脆站在前人的基础上进行拓展
- 设计目标(这里指html转图片的功能)
- 生成图片的宽可指定
- 支持对线上网页进行转图片
- 支持对html中指定的区域进行转换
- css样式渲染支持
本篇先会先实现一个基本的功能,即读去
markdown
文档, 并转为一张图片
利用之前封装的
MarkDown2HtmlWrapper
工具类
具体实现逻辑参考项目工程,和markdown转html博文
注意
- html 为 Document 属性
- autoW, autoH 用于控制是否自适应html实际的长宽
@Data
public class HtmlRenderOptions {
/**
* 输出图片的宽
*/
private Integer w;
/**
* 输出图片的高
*/
private Integer h;
/**
* 是否自适应宽
*/
private boolean autoW;
/**
* 是否自适应高
*/
private boolean autoH;
/**
* 输出图片的格式
*/
private String outType;
/**
* html相关内容
*/
private Document document;
}
同样采用Builder模式来进行配置项设置
public class Html2ImageWrapper {
private static DOMParser domParser;
static {
domParser = new DOMParser(new HTMLConfiguration());
try {
domParser.setProperty("http://cyberneko.org/html/properties/names/elems", "lower");
} catch (Exception e) {
throw new RuntimeException("Can't create HtmlParserImpl", e);
}
}
private HtmlRenderOptions options;
private Html2ImageWrapper(HtmlRenderOptions options) {
this.options = options;
}
private static Document parseDocument(String content) throws Exception {
domParser.parse(new InputSource(new StringReader(content)));
return domParser.getDocument();
}
public static Builder of(String html) {
return new Builder().setHtml(html);
}
public static Builder ofMd(MarkdownEntity entity) {
return new Builder().setHtml(entity);
}
public BufferedImage asImage() {
BufferedImage bf = HtmlRender.parseImage(options);
return bf;
}
public boolean asFile(String absFileName) throws IOException {
File file = new File(absFileName);
FileUtil.mkDir(file);
BufferedImage bufferedImage = asImage();
if (!ImageIO.write(bufferedImage, options.getOutType(), file)) {
throw new IOException("save image error!");
}
return true;
}
public String asString() throws IOException {
BufferedImage img = asImage();
return Base64Util.encode(img, options.getOutType());
}
@Getter
public static class Builder {
/**
* 输出图片的宽
*/
private Integer w = 600;
/**
* 输出图片的高度
*/
private Integer h;
/**
* true,根据网页的实际宽渲染;
* false, 则根据指定的宽进行渲染
*/
private boolean autoW = true;
/**
* true,根据网页的实际高渲染;
* false, 则根据指定的高进行渲染
*/
private boolean autoH = false;
/**
* 输出图片的格式
*/
private String outType = "jpg";
/**
* 待转换的html内容
*/
private MarkdownEntity html;
public Builder setW(Integer w) {
this.w = w;
return this;
}
public Builder setH(Integer h) {
this.h = h;
return this;
}
public Builder setAutoW(boolean autoW) {
this.autoW = autoW;
return this;
}
public Builder setAutoH(boolean autoH) {
this.autoH = autoH;
return this;
}
public Builder setOutType(String outType) {
this.outType = outType;
return this;
}
public Builder setHtml(String html) {
this.html = new MarkdownEntity();
return this;
}
public Builder setHtml(MarkdownEntity html) {
this.html = html;
return this;
}
public Html2ImageWrapper build() throws Exception {
HtmlRenderOptions options = new HtmlRenderOptions();
options.setW(w);
options.setH(h);
options.setAutoW(autoW);
options.setAutoH(autoH);
options.setOutType(outType);
if (fontColor != null) {
html.addDivStyle("style", "color:" + options.getFontColor());
}
html.addDivStyle("style", "width:" + w + ";");
html.addWidthCss("img");
html.addWidthCss("code");
options.setDocument(parseDocument(html.toString()));
return new Html2ImageWrapper(options);
}
}
}
上面的实现,有个需要注意的地方
如何将html格式的字符串,转为 Document 对象
利用了开源工具 nekohtml
, 可以较好的实现html标签解析,看一下DOMParse
的初始化过程
private static DOMParser domParser;
static {
domParser = new DOMParser(new HTMLConfiguration());
try {
domParser.setProperty("http://cyberneko.org/html/properties/names/elems",
"lower");
} catch (Exception e) {
throw new RuntimeException("Can't create HtmlParserImpl", e);
}
}
try语句块中的内容并不能缺少,否则最终的样式会错乱,关于 nekohtml
的使用说明,可以查阅相关教程
上面的封装,主要是HtmlRenderOptions
的构建,主要的渲染逻辑则在下面
利用 xhtmlrenderer
实现html的渲染
- 宽高的自适应
- 图片的布局,内容渲染
public class HtmlRender {
/**
* 输出图片
*
* @param options
* @return
*/
public static BufferedImage parseImage(HtmlRenderOptions options) {
int width = options.getW();
int height = options.getH() == null ? 1024 : options.getH();
Graphics2DRenderer renderer = new Graphics2DRenderer();
renderer.setDocument(options.getDocument(), options.getDocument().getDocumentURI());
Dimension dimension = new Dimension(width, height);
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics2D = GraphicUtil.getG2d(bufferedImage);
// 自适应修改生成图片的宽高
if (options.isAutoH() || options.getH() == null) {
// do layout with temp buffer
renderer.layout(graphics2D, new Dimension(width, height));
graphics2D.dispose();
Rectangle size = renderer.getMinimumSize();
final int autoWidth = options.isAutoW() ? (int) size.getWidth() : width;
final int autoHeight = (int) size.getHeight();
bufferedImage = new BufferedImage(autoWidth, autoHeight, BufferedImage.TYPE_INT_RGB);
dimension = new Dimension(autoWidth, autoHeight);
graphics2D = GraphicUtil.getG2d(bufferedImage);
}
renderer.layout(graphics2D, dimension);
renderer.render(graphics2D);
graphics2D.dispose();
return bufferedImage;
}
}
@Test
public void testParse() throws Exception {
String file = "md/tutorial.md";
MarkdownEntity html = MarkDown2HtmlWrapper.ofFile(file);
BufferedImage img = Html2ImageWrapper.ofMd(html)
.setW(600)
.setAutoW(false)
.setAutoH(true)
.setOutType("jpg")
.build()
.asImage();
ImageIO.write(img, "jpg", new File("/Users/yihui/Desktop/md.jpg"));
}
输出图片
然后演示一个对项目中实际的教程文档输出图片的动态示意图, 因为生成的图片特别特别长,所以就不贴输出的图片了,有兴趣的同学可以下载工程,实际跑一下看看
源markdown文件地址:
https://github.com/liuyueyi/quick-media/blob/master/doc/images/imgGenV2.md
相关博文 : Java 实现 markdown转Html
项目地址:https://github.com/liuyueyi/quick-media
个人博客:一灰的个人博客
公众号获取更多: