QR Code
QR码(Quick Response Code,快速矩阵图码)是二维码的一种,于1994年由日本汽车零组件大厂电装公司的原昌宏发明。QR码使用四种标准化编码模式(数字、字母数字、字节(二进制)和日文(Shift_JIS))来存储数据。QR码原创于日本,现已在世界各国广泛运用于手机读码操作。QR码比普通条形码具有快速读取和更大的存储资料容量,也无需要像一维条码般在扫描时需要直线对准扫描仪,应用范围包括产品跟踪、物品识别、文档管理、库存营销等等。
QR码最大资料容量(对于版本40) | |
---|---|
数字 | 最多7,089字符 |
字母 | 最多4,296字符 |
二进制数(8 bit) | 最多2,953 字节 |
日文汉字/片假名 | 最多1,817字符(采用Shift JIS) |
中文汉字 | 最多984字符(采用UTF-8) |
最多1,800字符(采用BIG5/GB2312) |
错误修正容量 | |
---|---|
L等级 | 可修正7%的字码 |
M等级 | 可修正15%的字码 |
Q等级 | 可修正25%的字码 |
H等级 | 可修正30%的字码 |
对于我们来说QR码并不陌生,不管是网购剁手,还是日常面对面交易或信息交互,都有它的身影。
Java实现QR码生成和解析
Maven引用zxing
通过修改pom.xml
文件,引入zxing核心com.google.zxing.core
及其javase扩展com.google.zxing.javase
两个依赖。写这篇文章的时候使用的是3.5.3版本。
<properties>
...
<zxing.version>3.5.3</zxing.version>
...
</properties>
<dependencies>
...
<!-- https://mvnrepository.com/artifact/com.google.zxing/core -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>${zxing.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.zxing/javase -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>${zxing.version}</version>
</dependency>
...
</dependencies>
若Maven编译报错:[ERROR] 不再支持源选项 5。请使用 7 或更高版本。[ERROR] 不再支持目标选项 5。请使用 7 或更高版本。加上源和目标编译器设置:
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
以maven-archetype-quickstart
脚手架搭建的maven
项目为例,完整的pom.xml
如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.humbunklung</groupId>
<artifactId>quick-response-code</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
<packaging>jar</packaging>
<name>quick-response-code</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<zxing.version>3.5.3</zxing.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.zxing/core -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>${zxing.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.zxing/javase -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>${zxing.version}</version>
</dependency>
</dependencies>
</project>
QR码图片生成
QR码编码,通过zxing
的MultiFormatWriter
的encode
方法完成,将需要编码的内容按照用户设置的参数形成比特矩阵。
/**
* 按照自定义内容、宽度、高度、纠错等级、字符集、编剧创建QR码比特矩阵。
*
* @param content 二维码内容
* @param width 二维码宽度
* @param height 二维码高度
* @param errCorrLevel 纠错等级,可以是<code>L, M, Q, H</code>
* @param charSet 字符集
* @param margin 边距
* @return BitMatrix 二维码比特矩阵
* @throws WriterException
*/
public static BitMatrix createQRCodeBitMatrix(String content, int width, int height,
ErrorCorrectionLevel errCorrLevel,
String charSet, int margin) throws WriterException {
if (width <= 0) {
width = DEFAULT_QR_CODE_WIDTH;
}
if (height <= 0) {
height = DEFAULT_QR_CODE_HEIGHT;
}
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.ERROR_CORRECTION, errCorrLevel); // 设置纠错等级
hints.put(EncodeHintType.CHARACTER_SET, charSet); // 设置编码方式
hints.put(EncodeHintType.MARGIN, margin); // 设置边距
return new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
}
为了使用方便,我们可以将生成的比特矩阵BitMatrix
转换成字节数组、base64
字节数组甚至base64
字符串:
/**
* 创建二维码,返回字节数组
*
* @param content 二维码内容
* @param imageFormat 二维码图像格式
* @param width 宽度
* @param height 高度
* @param errCorrLevel 纠错等级
* @param charSet 字符集
* @param margin 边距
* @return byte[]
* @throws WriterException
* @throws IOException
*/
public static byte[] createQRCode(String content, String imageFormat, int width, int height,
ErrorCorrectionLevel errCorrLevel, String charSet, int margin)
throws WriterException, IOException {
if (imageFormat == null || imageFormat.equals("")){
imageFormat = DEFAULT_FORMAT_NAME;
}
BitMatrix bitMatrix = createQRCodeBitMatrix(content, width, height, errCorrLevel, charSet, margin);
ByteArrayOutputStream os = new ByteArrayOutputStream();
MatrixToImageWriter.writeToStream(bitMatrix, imageFormat, os);
return os.toByteArray();
}
/**
* 创建二维码,返回base64字节流
*
* @param content 二维码里的内容
* @param imageFormat 图片后缀名
* @param width 二维码宽度
* @param height 二维码高度
* @return byte[] base64字节数组
*/
public static byte[] createQRCodeBase64Bytes(String content , String imageFormat , int width , int height)
throws WriterException, IOException {
byte[] bytes = createQRCode(content , imageFormat , width, height);
return Base64.getEncoder().encode(bytes);
}
/**
* 创建二维码,返回base64字符串
*
* @param content 二维码里的内容
* @param imageFormat 图片后缀名
* @param width 二维码宽度
* @param height 二维码高度
* @return String base64字符串
*/
public static String createQRCodeBase64(String content , String imageFormat , int width , int height,
String encoding) throws WriterException, IOException {
return new String(createQRCodeBase64Bytes(content, imageFormat, width, height), encoding);
}
为了使用方便,也可以将BitMatrix
转换为java.awt.BufferedImage
供后续使用:
/**
* 转换为BufferedImage
*
* @param bitMatrix 比特矩阵
* @param onColor 条码颜色
* @param offColor 背景颜色
* @return java.awt.image.BufferedImage
*/
public static BufferedImage toBufferedImage(BitMatrix bitMatrix, int onColor, int offColor) {
MatrixToImageConfig matrixToImageConfig = new MatrixToImageConfig(onColor, offColor);
return MatrixToImageWriter.toBufferedImage(bitMatrix, matrixToImageConfig);
}
其中,通过查询zxing
的文档,我们可以知道MatrxiToImageConfig
中的onColor
和offColor
分别是QR码的二维码颜色和背景色,当使用无参数构造方法时,默认为黑、白。
接下来,我们不妨用一个简单的方法来整合以上方法,供主程序调用:
/**
* 根据内容和设置生成二维码图片到指定路径
*
* @param content 二维码里的内容
* @param imagePath 生成图片路径
* @param imageFormat 图片格式
* @param onColor 二维码颜色
* @param offColor 背景颜色
* @param width 二维码宽度
* @param height 二维码高度
* @param errorCorrectionLevel 纠错等级
* @param charSet 字符集
* @param margin 边距
* @throws WriterException
* @throws IOException
*/
public static void encodeQRCode(String content, String imagePath,
String imageFormat, int onColor, int offColor, int width, int height,
ErrorCorrectionLevel errorCorrectionLevel, String charSet, int margin)
throws WriterException, IOException {
if (imageFormat == null || imageFormat.equals("")){
imageFormat = DEFAULT_FORMAT_NAME;
}
BufferedImage bufferedImage = encodeQRCode(content, onColor, offColor,
width, height, errorCorrectionLevel, charSet, margin);
ImageIO.write(bufferedImage, imageFormat, new File(imagePath));
}
通过在调用以上代码:
public static void encodeDemo() {
// 当前目录
String currentDirectory = System.getProperty("user.dir");
// 获取当前时间并设置格式,用于生成的文件名
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
// 输出目录
String output = String.format("%s/data/%s.png", currentDirectory, now.format(dateTimeFormatter));
String imagePath = new File(output).toPath().toString();
try {
QRCodeHelper.encodeQRCode(DEFAULT_TEST_CONTENT, imagePath, "png", 0xff5683ed, 0xffffffff,
500, 500, ErrorCorrectionLevel.H, "UTF-8", 2);
} catch (IOException e) {
e.printStackTrace();
} catch (WriterException e) {
e.printStackTrace();
}
}
可生成如下的二维码:
QR码图片解码
QR码的解码相对而言显得“朴实无华”,它通过MultiFormatReader
的decode
方法完成:
/**
* 解码二维码
*
* @param image 二维码BufferedImage
* @return java.lang.String
*/
public static String decodeQRCode(BufferedImage image) throws Exception {
if (image == null) return "";
BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>();
hints.put(DecodeHintType.CHARACTER_SET, DEFAULT_CHAR_SET);
Result result = new MultiFormatReader().decode(bitmap, hints);
return result.getText();
}
QR码嵌入LOGO
在日常生活中,我们常常见到有些二维码嵌入了LOGO,因此我们也可以在上述基础编码的基础上嵌入LOGO,因LOGO会对二维码构成遮挡,那么,前面所述的错误纠正等级,就可以起作用了,考虑不同的纠正等级和大小的二维码,我们可以适当控制嵌入图像的大小。实现代码举例如下:
/**
* 给BufferedImage添加LOGO
*
* @param bufferedImage
* @param logoFile
* @param xPos LOGO在二维码中的x坐标
* @param yPos LOGO在二维码中的y坐标
* @param logoWidth LOGO在二维码中的宽度
* @param logoHeight LOGO在二维码中的高度
* @param strokeWidth
* @param strokeCap
* @param strokeJoin
* @param arcWidth
* @param arcHeight
* @return
* @throws IOException
*/
public static BufferedImage addLogoToBufferedImage(BufferedImage bufferedImage, File logoFile,
int xPos, int yPos, int logoWidth, int logoHeight,
float strokeWidth, int strokeCap, int strokeJoin,
int arcWidth, int arcHeight) throws IOException {
BufferedImage clone = deepCopy(bufferedImage); // 深复制BufferedImage避免污染原图
Graphics2D graphics = clone.createGraphics();
// 读取logo图片文件
BufferedImage logo = ImageIO.read(logoFile);
// 开始绘制图片
graphics.drawImage(logo, xPos, yPos, logoWidth, logoHeight, null);
graphics.setStroke(new BasicStroke(strokeWidth, strokeCap, strokeJoin));
graphics.setColor(Color.white);
// 绘制圆角矩形
graphics.drawRoundRect(xPos, yPos, logoWidth, logoHeight, arcWidth, arcHeight);
graphics.dispose();
clone.flush();
return clone;
}
/**
* 在QR码图像中央加入一个1/5大小的LOGO
* @param bufferedImage QR码图像
* @param logoFile LOGO文件
* @param strokeWidth LOGO圆角笔宽
* @param strokeCap
* @param strokeJoin
* @param arcWidth 圆角宽,0则不圆
* @param arcHeight 圆角高,0则不圆
* @return
* @throws IOException
*/
public static BufferedImage addLogoToBufferedImage(BufferedImage bufferedImage, File logoFile,
float strokeWidth, int strokeCap, int strokeJoin,
int arcWidth, int arcHeight)
throws IOException {
int matrixWidth = bufferedImage.getWidth();
int matrixHeight = bufferedImage.getHeight();
// 读取logo图片文件
BufferedImage logo = ImageIO.read(logoFile);
int logoWidth = logo.getWidth();
int logoHeight = logo.getHeight();
// 计算logo放置位置
int x = matrixWidth / 5*2;
int y = matrixHeight / 5*2;
int width = matrixWidth / 5;
int height = matrixHeight / 5;
return addLogoToBufferedImage(bufferedImage, logoFile, x, y, width, height,
strokeWidth, strokeCap, strokeJoin, arcWidth, arcHeight);
}
调用实例如下:
public static void encodeWithLogoDemo() {
// 当前目录
String currentDirectory = System.getProperty("user.dir");
// 获取当前时间并设置格式,用于生成的文件名
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
// 输出目录
String output = String.format("%s/data/%s.png", currentDirectory, now.format(dateTimeFormatter));
String imagePath = new File(output).toPath().toString();
File logoFile = new File(String.format("%s/data/logo.jpg", currentDirectory));
try {
BufferedImage qr = QRCodeHelper.encodeQRCode(DEFAULT_TEST_CONTENT, 0xff5683ed, 0xffffffff,
500, 500, ErrorCorrectionLevel.H, "UTF-8", 2);
BufferedImage qrWithLogo = QRCodeHelper.addLogoToBufferedImage(qr, logoFile, 5.0F,
1, 1, 20, 20);
ImageIO.write(qr, "png", new File(imagePath));
ImageIO.write(qrWithLogo, "png",
new File(imagePath.replace(".png", "-logo.png")));
} catch (IOException e) {
e.printStackTrace();
} catch (WriterException e) {
e.printStackTrace();
}
}
生成的二维码如下图:
还有时候,我们会遇到一些像水印一样覆盖到基础QR码中的二维码图像:
LOGO像水印一样嵌入到二维码中,这种方式同样要考虑大小、透明度的问题,过大的LOGO如果不甚透明,是无法识别的,代码可以这样实现:
/**
* 给QR码的BufferedImage添加可透明的LOGO
*
* @param qrcode
* @param overlay
* @param overlayToQRCodeRatio
* @param overlayTransparency
* @return
*/
public static BufferedImage getQRCodeWithOverlay(BufferedImage qrcode, BufferedImage overlay,
float overlayToQRCodeRatio, float overlayTransparency) {
BufferedImage scaledOverlay = scaleOverlay(qrcode, overlay,overlayToQRCodeRatio);
Integer deltaHeight = qrcode.getHeight() - scaledOverlay.getHeight();
Integer deltaWidth = qrcode.getWidth() - scaledOverlay.getWidth();
BufferedImage combined = new BufferedImage(qrcode.getWidth(), qrcode.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = (Graphics2D)combined.getGraphics();
g2.drawImage(qrcode, 0, 0, null);
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, overlayTransparency));
g2.drawImage(scaledOverlay, Math.round(deltaWidth/2), Math.round(deltaHeight/2), null);
return combined;
}
/**
* 按比例调整LOGO
*
* @param qrcode
* @param overlay
* @param overlayToQRCodeRatio
* @return
*/
private static BufferedImage scaleOverlay(BufferedImage qrcode, BufferedImage overlay, float overlayToQRCodeRatio) {
Integer scaledWidth = Math.round(qrcode.getWidth() * overlayToQRCodeRatio);
Integer scaledHeight = Math.round(qrcode.getHeight() * overlayToQRCodeRatio);
BufferedImage imageBuff = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_ARGB);
Graphics g = imageBuff.createGraphics();
g.drawImage(overlay.getScaledInstance(scaledWidth, scaledHeight, BufferedImage.SCALE_SMOOTH),
0, 0, new Color(0,0,0), null);
g.dispose();
return imageBuff;
}
调用方法如下,我们不妨设置该LOGO占二维码80%大小,透明度只有0.1,即差不多全透明了,实测不影响扫码:
public static void encodeWithOverlayDemo() {
// 当前目录
String currentDirectory = System.getProperty("user.dir");
// 获取当前时间并设置格式,用于生成的文件名
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
// 输出目录
String output = String.format("%s/data/%s.png", currentDirectory, now.format(dateTimeFormatter));
String imagePath = new File(output).toPath().toString();
File logoFile = new File(String.format("%s/data/logo.jpg", currentDirectory));
try {
BufferedImage qr = QRCodeHelper.encodeQRCode(DEFAULT_TEST_CONTENT, 0xff5683ed, 0xffffffff,
500, 500, ErrorCorrectionLevel.H, "UTF-8", 2);
BufferedImage qrWithLogo = QRCodeHelper.getQRCodeWithOverlay(qr, ImageIO.read(logoFile),
0.8F, 0.1F);
ImageIO.write(qr, "png", new File(imagePath));
ImageIO.write(qrWithLogo, "png",
new File(imagePath.replace(".png", "-logo.png")));
} catch (IOException e) {
e.printStackTrace();
} catch (WriterException e) {
e.printStackTrace();
}
}
后记
二维码丰富了我们的生活,通过zxing等成熟的库,我们可以写出各类二维码应用。也通过这个库,完成了工作当中需要生成二维码的应用开发,文章中的代码和思路,吸收了网络上的一些资源,并作了修改,在此也向原作者表示感谢。最后修改的QRCodeHelper
类如下:
package org.humbunklung;
import java.awt.*;
import java.awt.image.ColorModel;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.Base64;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
/**
* QRCodeHelper
*
* @author yesy
*/
public class QRCodeHelper {
// 默认编码方式
private static final String DEFAULT_CHAR_SET = "UTF-8";
// 默认二维码图片格式
private static final String DEFAULT_FORMAT_NAME = "PNG";
// 默认二维码宽度
private static final int DEFAULT_QR_CODE_WIDTH = 300;
// 默认二维码高度
private static final int DEFAULT_QR_CODE_HEIGHT = 300;
public static BitMatrix createBitMatrix(String content) throws WriterException {
return createBitMatrix(content, DEFAULT_QR_CODE_WIDTH, DEFAULT_QR_CODE_WIDTH);
}
/**
* 创建BitMatrix比特矩阵
*
* @param content 二维码里的内容
* @param width 二维码宽度
* @param height 二维码高度
* @return com.google.zxing.common.BitMatrix 二维码比特矩阵
* @throws WriterException
*/
public static BitMatrix createBitMatrix(String content , int width , int height) throws WriterException {
if (width <= 0) {
width = DEFAULT_QR_CODE_WIDTH;
}
if (height <= 0) {
height = DEFAULT_QR_CODE_HEIGHT;
}
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); // 纠错等级L,M,Q,H
hints.put(EncodeHintType.CHARACTER_SET, DEFAULT_CHAR_SET);// 编码utf-8
hints.put(EncodeHintType.MARGIN, 1); // 边距
// 创建比特矩阵
return new MultiFormatWriter().encode(content,
BarcodeFormat.QR_CODE, width, height, hints);
}
/**
* 按照自定义内容、宽度、高度、纠错等级、字符集、编剧创建QR码比特矩阵。
*
* @param content 二维码内容
* @param width 二维码宽度
* @param height 二维码高度
* @param errCorrLevel 纠错等级,可以是<code>L, M, Q, H</code>
* @param charSet 字符集
* @param margin 边距
* @return BitMatrix 二维码比特矩阵
* @throws WriterException
*/
public static BitMatrix createQRCodeBitMatrix(String content, int width, int height,
ErrorCorrectionLevel errCorrLevel,
String charSet, int margin) throws WriterException {
if (width <= 0) {
width = DEFAULT_QR_CODE_WIDTH;
}
if (height <= 0) {
height = DEFAULT_QR_CODE_HEIGHT;
}
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.ERROR_CORRECTION, errCorrLevel); // 设置纠错等级
hints.put(EncodeHintType.CHARACTER_SET, charSet); // 设置编码方式
hints.put(EncodeHintType.MARGIN, margin); // 设置边距
return new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
}
/**
* 创建二维码,返回字节数组
*
* @param content 二维码里的内容
* @param imageFormat 图片后缀名
* @param width 二维码宽度
* @param height 二维码高度
* @return byte[] 二维码字节流
*/
public static byte[] createQRCode(String content , String imageFormat , int width , int height)
throws WriterException, IOException {
if (imageFormat == null || imageFormat.equals("")){
imageFormat = DEFAULT_FORMAT_NAME;
}
BitMatrix bitMatrix = createBitMatrix(content , width, height);
ByteArrayOutputStream os = new ByteArrayOutputStream();
MatrixToImageWriter.writeToStream(bitMatrix, imageFormat, os);
return os.toByteArray();
}
/**
* 创建二维码,返回字节数组
*
* @param content 二维码内容
* @param imageFormat 二维码图像格式
* @param width 宽度
* @param height 高度
* @param errCorrLevel 纠错等级
* @param charSet 字符集
* @param margin 边距
* @return byte[]
* @throws WriterException
* @throws IOException
*/
public static byte[] createQRCode(String content, String imageFormat, int width, int height,
ErrorCorrectionLevel errCorrLevel, String charSet, int margin)
throws WriterException, IOException {
if (imageFormat == null || imageFormat.equals("")){
imageFormat = DEFAULT_FORMAT_NAME;
}
BitMatrix bitMatrix = createQRCodeBitMatrix(content, width, height, errCorrLevel, charSet, margin);
ByteArrayOutputStream os = new ByteArrayOutputStream();
MatrixToImageWriter.writeToStream(bitMatrix, imageFormat, os);
return os.toByteArray();
}
/**
* 创建二维码,返回base64字节流
*
* @param content 二维码里的内容
* @param imageFormat 图片后缀名
* @param width 二维码宽度
* @param height 二维码高度
* @return byte[] base64字节数组
*/
public static byte[] createQRCodeBase64Bytes(String content , String imageFormat , int width , int height)
throws WriterException, IOException {
byte[] bytes = createQRCode(content , imageFormat , width, height);
return Base64.getEncoder().encode(bytes);
}
/**
* 创建二维码,返回base64字符串
*
* @param content 二维码里的内容
* @param imageFormat 图片后缀名
* @param width 二维码宽度
* @param height 二维码高度
* @return String base64字符串
*/
public static String createQRCodeBase64(String content , String imageFormat , int width , int height,
String encoding) throws WriterException, IOException {
return new String(createQRCodeBase64Bytes(content, imageFormat, width, height), encoding);
}
/**
* 转换为BufferedImage
*
* @param bitMatrix 比特矩阵
* @param onColor 条码颜色
* @param offColor 背景颜色
* @return java.awt.image.BufferedImage
*/
public static BufferedImage toBufferedImage(BitMatrix bitMatrix, int onColor, int offColor) {
MatrixToImageConfig matrixToImageConfig = new MatrixToImageConfig(onColor, offColor);
return MatrixToImageWriter.toBufferedImage(bitMatrix, matrixToImageConfig);
}
/**
* 转换为BufferedImage
*
* @param bitMatrix 比特矩阵
* @return java.awt.image.BufferedImage
*/
public static BufferedImage toBufferedImage(BitMatrix bitMatrix) {
// MatrixToImageConfig matrixToImageConfig = new MatrixToImageConfig(0xFF000001, 0xFFFFFFFF);
MatrixToImageConfig matrixToImageConfig = new MatrixToImageConfig(); // 无参数为白底黑码
return MatrixToImageWriter.toBufferedImage(bitMatrix, matrixToImageConfig);
}
/**
* QR码编码
*
* @param content 编码内容
* @param onColor 二维码颜色
* @param offColor 背景颜色
* @param width 宽度
* @param height 高度
* @param errCorrLevel 纠错等级
* @param charSet 字符集
* @param margin 边距
* @return BufferedImage
* @throws WriterException
*/
public static BufferedImage encodeQRCode(String content, int onColor, int offColor, int width, int height,
ErrorCorrectionLevel errCorrLevel, String charSet, int margin)
throws WriterException {
BitMatrix bitMatrix = createQRCodeBitMatrix(content, width, height, errCorrLevel, charSet, margin);
return toBufferedImage(bitMatrix, onColor, offColor);
}
/**
* QR码编码
*
* @param content 编码内容
* @param width 宽度
* @param height 高度
* @return BufferedImage
* @throws WriterException
*/
public static BufferedImage encodeQRCode(String content, int width, int height) throws WriterException {
BitMatrix bitMatrix = createBitMatrix(content, width, height);
return toBufferedImage(bitMatrix);
}
/**
* QR码编码
*
* @param content 要编码的内容
* @return BufferedImage
* @throws WriterException
*/
public static BufferedImage encodeQRCode(String content) throws WriterException {
return toBufferedImage(createBitMatrix(content));
}
/**
* 根据内容和设置生成二维码图片到指定路径
*
* @param content 二维码里的内容
* @param imagePath 生成图片路径
* @param imageFormat 图片格式
* @param onColor 二维码颜色
* @param offColor 背景颜色
* @param width 二维码宽度
* @param height 二维码高度
* @param errorCorrectionLevel 纠错等级
* @param charSet 字符集
* @param margin 边距
* @throws WriterException
* @throws IOException
*/
public static void encodeQRCode(String content, String imagePath,
String imageFormat, int onColor, int offColor, int width, int height,
ErrorCorrectionLevel errorCorrectionLevel, String charSet, int margin)
throws WriterException, IOException {
if (imageFormat == null || imageFormat.equals("")){
imageFormat = DEFAULT_FORMAT_NAME;
}
BufferedImage bufferedImage = encodeQRCode(content, onColor, offColor,
width, height, errorCorrectionLevel, charSet, margin);
ImageIO.write(bufferedImage, imageFormat, new File(imagePath));
}
/**
* 解码二维码
*
* @param image 二维码BufferedImage
* @return java.lang.String
*/
public static String decodeQRCode(BufferedImage image) throws Exception {
if (image == null) return "";
BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>();
hints.put(DecodeHintType.CHARACTER_SET, DEFAULT_CHAR_SET);
Result result = new MultiFormatReader().decode(bitmap, hints);
return result.getText();
}
/**
* 给BufferedImage添加LOGO
*
* @param bufferedImage
* @param logoFile
* @param xPos LOGO在二维码中的x坐标
* @param yPos LOGO在二维码中的y坐标
* @param logoWidth LOGO在二维码中的宽度
* @param logoHeight LOGO在二维码中的高度
* @param strokeWidth
* @param strokeCap
* @param strokeJoin
* @param arcWidth
* @param arcHeight
* @return
* @throws IOException
*/
public static BufferedImage addLogoToBufferedImage(BufferedImage bufferedImage, File logoFile,
int xPos, int yPos, int logoWidth, int logoHeight,
float strokeWidth, int strokeCap, int strokeJoin,
int arcWidth, int arcHeight) throws IOException {
BufferedImage clone = deepCopy(bufferedImage); // 深复制BufferedImage避免污染原图
Graphics2D graphics = clone.createGraphics();
// 读取logo图片文件
BufferedImage logo = ImageIO.read(logoFile);
// 开始绘制图片
graphics.drawImage(logo, xPos, yPos, logoWidth, logoHeight, null);
graphics.setStroke(new BasicStroke(strokeWidth, strokeCap, strokeJoin));
graphics.setColor(Color.white);
// 绘制圆角矩形
graphics.drawRoundRect(xPos, yPos, logoWidth, logoHeight, arcWidth, arcHeight);
graphics.dispose();
clone.flush();
return clone;
}
/**
* 在QR码图像中央加入一个1/5大小的LOGO
* @param bufferedImage QR码图像
* @param logoFile LOGO文件
* @param strokeWidth LOGO圆角笔宽
* @param strokeCap
* @param strokeJoin
* @param arcWidth 圆角宽,0则不圆
* @param arcHeight 圆角高,0则不圆
* @return
* @throws IOException
*/
public static BufferedImage addLogoToBufferedImage(BufferedImage bufferedImage, File logoFile,
float strokeWidth, int strokeCap, int strokeJoin,
int arcWidth, int arcHeight)
throws IOException {
int matrixWidth = bufferedImage.getWidth();
int matrixHeight = bufferedImage.getHeight();
// 读取logo图片文件
BufferedImage logo = ImageIO.read(logoFile);
int logoWidth = logo.getWidth();
int logoHeight = logo.getHeight();
// 计算logo放置位置
int x = matrixWidth / 5*2;
int y = matrixHeight / 5*2;
int width = matrixWidth / 5;
int height = matrixHeight / 5;
return addLogoToBufferedImage(bufferedImage, logoFile, x, y, width, height,
strokeWidth, strokeCap, strokeJoin, arcWidth, arcHeight);
}
/**
* 给BufferedImage添加LOGO,该方法选择自网络,修改了原来按引用传递带来的副作用(会改变原来的BufferedImage)。
*
* @deprecated
* @param bufferedImage
* @param logoFile
* @return
* @throws IOException
*/
public static BufferedImage addLogoToBufferedImage(BufferedImage bufferedImage, File logoFile)
throws IOException {
BufferedImage clone = deepCopy(bufferedImage); // 深复制BufferedImage避免污染原图
Graphics2D graphics = clone.createGraphics();
int matrixWidth = clone.getWidth();
int matrixHeight = clone.getHeight();
// 读取logo图片文件
BufferedImage logo = ImageIO.read(logoFile);
int logoWidth = logo.getWidth();
int logoHeight = logo.getHeight();
// 计算logo放置位置
int x = clone.getWidth() / 5*2;
int y = clone.getHeight() / 5*2;
int width = matrixWidth / 5;
int height = matrixHeight / 5;
// 开始绘制图片
graphics.drawImage(logo, x, y, width, height, null);
graphics.setStroke(new BasicStroke(5.0F, 1, 1));
graphics.setColor(Color.white);
graphics.drawRoundRect(x, y, width, height, 15, 15);
graphics.dispose();
clone.flush();
return clone;
}
/**
* 给QR码的BufferedImage添加可透明的LOGO
*
* @param qrcode
* @param overlay
* @param overlayToQRCodeRatio
* @param overlayTransparency
* @return
*/
public static BufferedImage getQRCodeWithOverlay(BufferedImage qrcode, BufferedImage overlay,
float overlayToQRCodeRatio, float overlayTransparency) {
BufferedImage scaledOverlay = scaleOverlay(qrcode, overlay,overlayToQRCodeRatio);
Integer deltaHeight = qrcode.getHeight() - scaledOverlay.getHeight();
Integer deltaWidth = qrcode.getWidth() - scaledOverlay.getWidth();
BufferedImage combined = new BufferedImage(qrcode.getWidth(), qrcode.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = (Graphics2D)combined.getGraphics();
g2.drawImage(qrcode, 0, 0, null);
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, overlayTransparency));
g2.drawImage(scaledOverlay, Math.round(deltaWidth/2), Math.round(deltaHeight/2), null);
return combined;
}
/**
* 按比例调整LOGO
*
* @param qrcode
* @param overlay
* @param overlayToQRCodeRatio
* @return
*/
private static BufferedImage scaleOverlay(BufferedImage qrcode, BufferedImage overlay, float overlayToQRCodeRatio) {
Integer scaledWidth = Math.round(qrcode.getWidth() * overlayToQRCodeRatio);
Integer scaledHeight = Math.round(qrcode.getHeight() * overlayToQRCodeRatio);
BufferedImage imageBuff = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_ARGB);
Graphics g = imageBuff.createGraphics();
g.drawImage(overlay.getScaledInstance(scaledWidth, scaledHeight, BufferedImage.SCALE_SMOOTH),
0, 0, new Color(0,0,0), null);
g.dispose();
return imageBuff;
}
/**
* BufferedImage 深复制
* @param bi
* @return
*/
private static BufferedImage deepCopy(BufferedImage bi) {
ColorModel cm = bi.getColorModel();
boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
WritableRaster raster = bi.copyData(bi.getRaster().createCompatibleWritableRaster());
return new BufferedImage(cm, raster, isAlphaPremultiplied, null);
}
}
因写得仓促,肯定有不少漏洞,希望大家指正,也希望大家不吝扫码、关注、点赞。