JFinal使用技巧-JSON画图卡片工具

如题 JSON参数画图生成图片的一个工具 , 场景:  用于卡片制作, 学生证, 毕业证等各种证书的下载打印等业务,   以及礼品卡的制作,  小程序页等转图片后 分享朋友圈等等.... 
不多说, 直接上石马 : 

DrawingRender.java 去年写的工具...今天被同事用到了, 并且根据他提了一个居中的需求, 我做了一下增强支持ElKit.eval表达式了

package com.momathink.common.plugin.draw;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jfinal.kit.ElKit;
import com.jfinal.kit.Kv;
import com.jfinal.kit.PathKit;
import com.jfinal.kit.StrKit;
import com.jfinal.render.Render;
import com.jfinal.render.RenderException;

/**
 * 卡片制作
 *
 * @author 杜福忠 2017-9-5
 */
public class DrawingRender extends Render {

    protected BufferedImage image = null;
    protected String cardName = null;

    public DrawingRender(String json) {
        imageByJson(JSON.parseObject(json));
    }

    @Override
    public void render() {

        response.setHeader("Content-Disposition", getFilename(request));
        response.setHeader("Pragma","no-cache");
        response.setHeader("Cache-Control","no-cache");
        response.setDateHeader("Expires", 0);
        response.setContentType("image/jpeg");

        ServletOutputStream sos = null;
        try {
            sos = response.getOutputStream();
            ImageIO.write(image, "jpeg", sos);
        } catch (IOException e) {
            if (getDevMode()) {
                throw new RenderException(e);
            }
        } catch (Exception e) {
            throw new RenderException(e);
        } finally {
            if (sos != null) {
                try {sos.close();} catch (IOException e) {e.printStackTrace();}
            }
        }

    }

    /**
     * 解析 卡片
     *
     * <pre>
    {
        "name":"卡片生成后的名称"
        "path":"背景图片地址"
        "list":[{
                    "type":"image",
                    "path" : "图片地址"
                    "x" : "50",
                    "y" : "50"
                },{
                    "type":"word",
                    "value" : "字体内容",
                    "x" : "100",
                    "y" : "100"
                    "font" : {
                        "name" : "Dialog、DialogInput、Monospaced、Serif 或 SansSerif,空为默认字体",
                        "style" : "0,1,2",
                        "size" : "1-70"
                    },
                    "color" : {
                        "r":"0到255",
                        "g":"0到255",
                        "b":"0到255",
                    }
                }
            ]
    }
     * </pre>
     *
     */
    protected BufferedImage imageByJson(JSONObject configure) {
        String basepath = PathKit.getWebRootPath();
        BufferedImage background = getImage(basepath + configure.getString("path"));
        if (background == null) {
            throw new NullPointerException("没有找到 背景图片");
        }
        cardName = isNull(configure.getString("name"), "Card");
        // 默认的验证码大小
        Integer width = background.getWidth();
        Integer height = background.getHeight();

        image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        // 获取图形上下文
        Graphics2D g = image.createGraphics();

        //呈现质量和总时间/质量折衷的控制
        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
        // 图形抗锯齿
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        // 字体抗锯齿
        g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

        // 设定默认背景色
        //g.setColor(new Color(255, 255, 255));
        //g.fillRect(0, 0, width, height);
        //背景图
        g.drawImage(background, 0, 0, width, height, imageobserver);

        //字符内容处理
        //设定字体颜色
        g.setColor(new Color(0, 0 , 0));

        // 处理单元素
        try {
            setList(configure, basepath, g);
        } catch (Exception e) {
            print(e);
        }

        // 销毁图像
        g.dispose();

        return image;
    }

    /**
     * @param configure
     * @param basepath
     * @param g
     */
    protected void setList(JSONObject configure, String basepath, Graphics2D g) {
        // list
        JSONArray jsonArray = configure.getJSONArray("list");
        for (Integer i = 0; i < jsonArray.size(); i++) {
            JSONObject element = jsonArray.getJSONObject(i);

            String type = element.getString("type");
            if ("word".equals(type)) {
                // 文字
                String value = element.getString("value");
                if (StrKit.isBlank(value)) {
                    continue;
                }

                String x = element.getString("x");
                String y = element.getString("y");

                // 文字
                JSONObject color = element.getJSONObject("color");
                Integer cr = color.getInteger("r");
                Integer cg = color.getIntValue("g");
                Integer cb = color.getIntValue("b");

                // 字体样式
                JSONObject font = element.getJSONObject("font");
                String fontname = font.getString("name");
                Integer fontStyle = font.getIntValue("style");
                String fontSize = font.getString("size");

                // 渲染
                setStr(g, cr, cg, cb, fontname, fontStyle, fontSize, value, x, y);
            } else if ("image".equals(type)) {
                // 图片
                String path = element.getString("path");
                if (StrKit.isBlank(path)) {
                    continue;
                }

                String x = element.getString("x");
                String y = element.getString("y");
                // 渲染
                setImage(g, basepath + path, x, y);
            }
        }
    }

    /**
     * //设定字体 //设定字体颜色 //设定字
     */
    protected void setStr(Graphics2D g, Integer colorR, Integer colorG, Integer colorB,
            String fontname, Integer fontStyle, String fontSize, String v, String x, String y) {
        colorR = isNull(colorR, 0);
        colorG = isNull(colorG, 0);
        colorB = isNull(colorB, 0);
        fontname = isNull(fontname, null);
        fontStyle = isNull(fontStyle, 1);
        fontSize = isNull(fontSize, "24");
        int centerX = image.getWidth() / 2;
        x = isNull(x, "0");
        y = isNull(y, "0");
        try {
            Integer fontSizei = ElKit.eval(fontSize,
                    Kv.by("length", v.length()).set("centerX", centerX));
            Font font = new Font(fontname, fontStyle, fontSizei);

            FontMetrics fontMetrics = g.getFontMetrics(font);
            int textWidth = fontMetrics.stringWidth(v);
            Kv data = Kv.by("length", v.length()).set("fontSize", fontSizei).set("str", v)
                    .set("centerX", centerX).set("textWidth", textWidth)
                    .set("center", centerX - textWidth / 2);
            Integer xi = ElKit.eval(x, data);
            Integer yi = ElKit.eval(y, data);

            g.setColor(new Color(colorR, colorG, colorB));
            g.setFont(font);
            g.drawString(v, xi, yi);
        } catch (Exception e) {
            print(e);
        }
    }

    /**
     * 画图片
     *
     * @param g
     * @param path
     * @param x
     * @param y
     */
    protected void setImage(Graphics2D g, String path, String x, String y) {
        x = isNull(x, "0");
        y = isNull(y, "0");
        int centerX = image.getWidth() / 2;
        try {

            BufferedImage img = getImage(path);
            Kv data = Kv.by("img", img).set("context", image).set("center",
                    centerX - img.getWidth() / 2);
            Integer xi = ElKit.eval(x, data);
            Integer yi = ElKit.eval(y, data);
            g.drawImage(img, xi, yi, imageobserver);
        } catch (Exception e) {
            print(e);
        }
    }

    protected Integer isNull(Integer val, Integer df) {
        return val != null ? val : df;
    }

    protected String isNull(String val, String df) {
        return StrKit.notBlank(val) ? val : df;
    }

    protected BufferedImage getImage(String imgPath) {
        try {
            return ImageIO.read(new FileInputStream(imgPath));
        } catch (IOException e) {
            print(e);
        }
        return null;
    }

    protected ImageObserver imageobserver = new ImageObserver() {
        @Override
        public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
            return true;
        }
    };

    protected String getFilename(HttpServletRequest request) {
        try {
            String agent = request.getHeader("USER-AGENT");
            if (agent.toLowerCase().indexOf("firefox") > 0) {
                cardName = new String(cardName.getBytes("utf-8"), "iso-8859-1");
            } else {
                cardName = URLEncoder.encode(cardName, "UTF-8");
            }
        } catch (UnsupportedEncodingException e) {
            print(e);
        }
        return "attachment; filename=" + cardName + ".jpg";
    }

    protected void print(Exception e) {
        e.printStackTrace();
    }


}

上面依赖的JAR包 有fastjson解析工具


工具完成, 开始使用:

/**
 * 工具
 *
 * @author 杜福忠 2018-09-07 14:02:52
 *
 */
@Clear()
public class KitController extends Controller {

    /**
     * 画板 /api/kit/drawing?data=JSON
     * 也可使用  cache=key
     */
    public void drawing() {
        String data = getPara("data");
        if (data == null) {
        	data = ConfigKit.getCache().get("apiKitCache", getPara("cache"));
		}
        render(new DrawingRender(data));
    }
    
    /**
     * 画板缓存参数 /api/kit/cache?data=JSON
     * 返回缓存key
     */
    public void cache() {
    	String data = getPara("data");
    	String key = StrKit.getRandomUUID();
		ConfigKit.getCache().put("apiKitCache", key, data);
		renderJson(key);
	}
}

上面依赖的JAR包 有ehcacheh缓存工具

用法:
如调试参数时使用的例子drawing.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JSON画图工具</title>
<style type="text/css">
.container {
    display: table;
    width: 80%;
}
 
.container .left,
.container .right {
    display: table-cell;
}
 
.container .left {
    width: 20%;
}
 
.container .right {
    width: 80%;
}
</style>
</head>
<body>
<div class="container">
    <div class="left">
<textarea rows="40" cols="80">
{
        "name":"卡片生成后的名称"
        "path":"/背景图片地址"
        "list":[{
                    "type":"image",
                    "path" : "/图片地址"
                    "x" : "50",
                    "y" : "50"
                },{
                    "type":"word",
                    "value" : "字体内容",
                    "x" : "100 center是居中",
                    "y" : "100"
                    "font" : {
                        "name" : "Dialog、DialogInput、Monospaced、Serif 或 SansSerif,空为默认字体",
                        "style" : "0,1,2",
                        "size" : "1-70"
                    },
                    "color" : {
                        "r":"0到255",
                        "g":"0到255",
                        "b":"0到255",
                    }
                }
            ]
}
</textarea>
    </div>
    <div class="right">
		<img alt="预览图" src="" >
    </div>
</div>

<script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
<script type="text/javascript">
$("textarea").keyup(function(){
	var json = $("textarea").val();
	$("img").attr("src", "/api/kit/drawing?data=" + json);
});
</script>
</body>
</html>

上面例子需要 自己调整一下参数 根据自己情况来写, 有说用canvas画的, 我反手就是一下兼容性, 易用性, 普适性啦... 23333 喜欢就好~

再比如小程序中的调用方式:

var jsonData = `
JSON 参数此处 省略
`;
wx.request({  url: 域名+'/api/kit/cache', //仅为示例,并非真实的接口地址
  data: {data:jsonData},success: function(res) {
      // 此处 修改一下  IMG 的 SRC=域名+"/api/kit/drawing?cache="+res.data 即可
      // 或者调用下载API
  }
})

预览一下:

image.png

点个赞呗~

评论区

不要香菜

2018-09-11 21:36

墙都不扶,就服你

欲风217

2018-09-12 08:04

赞赞赞~!!!

2018-09-12 09:08

可以实现类似于水印的功能?

杜福忠

2018-09-12 09:26

@枫 必须啊, 水印可以是文字还可以是LOGO图片的

xiaobinbin

2018-09-12 10:50

很不幸我就是楼主说的那个同事

JFinal

2018-09-12 11:14

感觉这个功能很神奇啊,进去的是 json ,出来的是图片,还支持水印、logo

而且还支持对 json 数据 key-value 的 value 部分支持表达式语言,表达式语言上了以后不仅仅是加减乘除数值运算的功能了, 打通式 java 方法调用、shared function、shared method、shared object 等等功能全有了

收藏加点赞是必须的

netwild

2018-09-13 14:19

niubility!!!

杜福忠

2018-09-13 18:20

@JFinal 还是JFinal Template强!

小路哥

2018-09-28 16:47

能不能艺术文字水印

杜福忠

2018-09-30 00:21

@小路哥 可以的, 注意字体安装在 java 的 jre\lib\fonts 里面, JSON的 font" : {"name": 你字体的名字如:"宋体" 就可以了

小路哥

2018-09-30 09:52

@杜福忠 好,谢谢

或是的话

2018-10-18 15:40

支持条码吗

杜福忠

2018-10-18 19:27

@或是的话
JFinal使用技巧-生成条形码
理论可以, 但是不如这个工具方便

北流家园网

2019-05-29 15:11

还不知道怎么用,先收藏

jfinal009

2020-07-02 12:18

@杜福忠 ConfigKit这个类在那个包,如何获取到,谢谢。

杜福忠

2020-07-02 13:46

@jfinal009 不好意思那个是我们项目里面封装的类,分享的时候没注意踢掉, 可以直接用 CacheKit.就可以了

jfinal009

2020-07-03 12:21

@杜福忠 功能强大,谢谢分享。

热门分享

扫码入社