彩色二维码生成,支持网络图标,可以指定颜色

基于 http://www.jfinal.com/share/361 修改,调用样例

render(new PhotoQrCode("xxx", 300, 300,"http://www.gtdblog.com/wp/wp-content/uploads/2017/09/meyou4-1.png",0xff9966));

test.png

源码

package com.rm.controller;

import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import javax.imageio.ImageIO;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.jfinal.kit.StrKit;
import com.jfinal.render.Render;
import com.jfinal.render.RenderException;

public class PhotoQrCode extends Render {

	private static MatrixToImageConfig DEFAULT_CONFIG = new MatrixToImageConfig();

	private String content;
	private int width;
	private int height;
	private ErrorCorrectionLevel errorCorrectionLevel;

	/**
	 * 构造方法,经测试不指定纠错参数时,默认使用的是 'L' 最低级别纠错参数
	 * 
	 * @param content
	 *            二维码携带内容
	 * @param width
	 *            二维码宽度
	 * @param height
	 *            二维码高度
	 */
	public PhotoQrCode(String content, int width, int height) {
		init(content, width, height, ErrorCorrectionLevel.H);
	}

	/**
	 * 黑白图标
	 * 
	 * @param content
	 * @param width
	 * @param height
	 * @param logoUrl
	 *            网络图标地址
	 */
	public PhotoQrCode(String content, int width, int height, String logoUrl) {
		init(content, width, height, ErrorCorrectionLevel.H);
		url = logoUrl;
	}

	/**
	 * 指定二维码颜色,白色背景
	 * 
	 * @param content
	 * @param width
	 * @param height
	 * @param logoUrl
	 *            网络图标地址
	 * @param onColor
	 *            二维码颜色
	 */
	public PhotoQrCode(String content, int width, int height, String logoUrl, int onColor) {
		init(content, width, height, ErrorCorrectionLevel.H);
		url = logoUrl;
		onColor = (((onColor >> 24) & 0xff) > 0) ? onColor : onColor | 0xff000000;
		DEFAULT_CONFIG = new MatrixToImageConfig(onColor, 0xffffff);
	}

	/**
	 * 指定前景 背景颜色
	 * 
	 * @param content
	 * @param width
	 * @param height
	 * @param logoUrl
	 * @param onColor
	 * @param offColor
	 */
	public PhotoQrCode(String content, int width, int height, String logoUrl, int onColor, int offColor) {
		init(content, width, height, ErrorCorrectionLevel.H);
		url = logoUrl;
		DEFAULT_CONFIG = new MatrixToImageConfig(onColor, offColor);
	}

	private String url;

	private void init(String content, int width, int height, ErrorCorrectionLevel errorCorrectionLevel) {
		if (StrKit.isBlank(content)) {
			throw new IllegalArgumentException("content 不能为空");
		}
		if (width < 0 || height < 0) {
			throw new IllegalArgumentException("width 与 height 不能小于 0");
		}
		this.content = content;
		this.width = width;
		this.height = height;
		this.errorCorrectionLevel = errorCorrectionLevel;
	}

	@Override
	public void render() {
		response.setHeader("Pragma", "no-cache");
		response.setHeader("Cache-Control", "no-cache");
		response.setDateHeader("Expires", 0);
		response.setContentType("image/png");

		Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();
		hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
		hints.put(EncodeHintType.MARGIN, 0); // 去掉白色边框,极度重要,否则二维码周围的白边会很宽
		if (errorCorrectionLevel != null) {
			hints.put(EncodeHintType.ERROR_CORRECTION, errorCorrectionLevel);
		}

		try {
			QRCodeWriter writer = new QRCodeWriter();
			BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, width, height, hints);

			// 经测试 200 X 200 大小的二维码使用 "png" 格式只有 412B,而 "jpg" 却达到 15KB
			// String path =
			// request.getSession().getServletContext().getRealPath("/static/QrCodeImg/logo.png");
			overlapImage(bitMatrix, "png", response.getOutputStream(), url);
		} catch (Exception e) {
			throw new RenderException(e);
		}
	}

	/**
	 * 将照片logo添加到二维码中间
	 * 
	 * @param matrix
	 * @param format
	 * @param stream
	 * @param logoPath
	 * @throws IOException
	 */
	public void overlapImage(BitMatrix matrix, String format, OutputStream stream, String logoPath) throws IOException {
		BufferedImage image = MatrixToImageWriter.toBufferedImage(matrix, DEFAULT_CONFIG);
		if (logoPath != null) {
            // ImageIO.read(new File(logoPath));
			BufferedImage logo = ImageIO.read(new URL(logoPath).openStream());

			Graphics2D g = image.createGraphics();
			// 考虑到logo照片贴到二维码中,建议大小不要超过二维码的1/5;
			int width = image.getWidth() / 3;
			int height = image.getHeight() / 3;
			// logo起始位置,此目的是为logo居中显示
			int x = (image.getWidth() - width) / 2;
			int y = (image.getHeight() - height) / 2;
			// 绘制图
			g.drawImage(logo, x, y, width, height, null);
			g.dispose();// 清理内存中的图片,返还内存给系统
			// 输出二维码
		}
		ImageIO.write(image, format, stream);
	}
}


评论区

JFinal

2017-09-25 10:41

非常有价值的实现,这个确实很多需要,二维码放上 logo 确实好看多了,感谢分享

1040110333

2017-09-25 14:51

可以

马小酱

2017-09-27 09:48

感谢分享

peterpeter

2017-09-28 18:26

这个是jfinal3.1还是3.2? 我的是3.1的可否?

peterpeter

2017-09-28 19:25

提取本地图片不可以,只能提取http://**.png才能生成二维码

JFinal

2017-09-28 23:02

@peterpeter 3.1 也可以,强烈建议从 3.1 升到 3.2, 因为这个版本升到 3.2 几乎没有影响,带来的全是好处,下载首页的 jfinal 3.2 changelog , 看看功能的提升。以及根据 jfinal 3.2 手册的最后一章进行升级,对于多数用户来说升级不需要改代码

溪碧旗

2017-10-10 13:55

C20-weice

2018-03-05 17:46

@peterpeter 本地图片可以,只需把overlapImage方法中的BufferedImage logo = ImageIO.read(new URL(logoPath).openStream());改为:BufferedImage logo = ImageIO.read(new FileInputStream(logoPath));即可。另外,注意图片格式。

peterpeter

2018-03-30 17:11

@溪碧旗 JPG图片不支持,com.jfinal.render.RenderException: javax.imageio.IIOException: Unsupported Image Type

阿尔法狗

2020-04-14 21:40

学到了

热门分享

扫码入社