前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java 实现 markdown转Image

Java 实现 markdown转Image

作者头像
一灰灰blog
发布2018-02-06 15:58:43
2K0
发布2018-02-06 15:58:43
举报
文章被收录于专栏:小灰灰小灰灰

markdown 转 image

前段时间实现了长图文生成的基本功能,然后想了下能否有个进阶版,直接将markdown生成渲染后的图片呢?

思路

有不少的库可以将 markdown 转为 html,那么这个需求就可以转为 html转Image了

1. markdown 转 html

可以参看之前的博文《Java 实现 markdown转Html》

2. html 转 图片

主要的核心问题就在这里了,如何实现html转图片?

  • 直接实现html转图片的包没怎么见,看到一个 html2image, 还不太好用
  • 在 AWT or Swing 的Panel上显示网页,在把Panel输出为 image 文件
  • 使用js相关技术实现转换

本篇博文具体实现以 html2image 的实现逻辑作为参考,然后定制实现一把(后面有机会写一篇利用js来实现html转图片的博文)

html2image 的实现原理

html2image 基本上没啥维护了,内部主要是利用了 xhtmlrender 实现html渲染为图片

代码语言:javascript
复制
Graphics2DRenderer renderer = new Graphics2DRenderer();
// 设置渲染内容
renderer.setDocument(document, document.getDocumentURI());

// 获取Graphics2D
graphics2D = bufferedImage.createGraphics();
renderer.layout(graphics2D, dimension);

// 内容渲染
renderer.render(graphics2D);
说明
  1. 为什么并不直接使用 java-html2image ?
  2. 因为有些定制的场景支持得不太友好,加上源码也比较简单,所以干脆站在前人的基础上进行拓展
  3. 设计目标(这里指html转图片的功能)
  4. 生成图片的宽可指定
  5. 支持对线上网页进行转图片
  6. 支持对html中指定的区域进行转换
  7. css样式渲染支持

实现

本篇先会先实现一个基本的功能,即读去markdown文档, 并转为一张图片

1. markdown 转 html 封装

利用之前封装的 MarkDown2HtmlWrapper 工具类

具体实现逻辑参考项目工程,和markdown转html博文

2. html 转 image

参数配置项 HtmlRenderOptions

注意

  • html 为 Document 属性
  • autoW, autoH 用于控制是否自适应html实际的长宽
代码语言:javascript
复制
@Data
public class HtmlRenderOptions {

    /**
     * 输出图片的宽
     */
    private Integer w;


    /**
     * 输出图片的高
     */
    private Integer h;


    /**
     * 是否自适应宽
     */
    private boolean autoW;


    /**
     * 是否自适应高
     */
    private boolean autoH;


    /**
     * 输出图片的格式
     */
    private String outType;

    /**
     * html相关内容
     */
    private Document document;
}

封装处理类

同样采用Builder模式来进行配置项设置

代码语言:javascript
复制
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 的初始化过程

代码语言:javascript
复制
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的渲染

  • 宽高的自适应
  • 图片的布局,内容渲染
代码语言:javascript
复制
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;
    }
}

测试

代码语言:javascript
复制
@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

个人博客:一灰的个人博客

参考博文

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • markdown 转 image
    • 思路
      • 1. markdown 转 html
      • 2. html 转 图片
    • 实现
      • 1. markdown 转 html 封装
      • 2. html 转 image
      • 封装处理类
      • 渲染
    • 测试
      • 其他
        • 参考博文
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档