pom.xml
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.15</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.3.3</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.3.3</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
</dependency>
ZxingUtils
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import com.google.zxing.EncodeHintType;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.google.zxing.qrcode.encoder.ByteMatrix;
import com.google.zxing.qrcode.encoder.Encoder;
import com.google.zxing.qrcode.encoder.QRCode;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class ZxingUtils {
public static String generateAsBase64(String content, String logoPath) {
QrConfig config = new QrConfig(100, 100);
config.setMargin(1);
config.setErrorCorrection(ErrorCorrectionLevel.H);
config.setImg(logoPath);
return QrCodeUtil.generateAsBase64(content, config, "png");
}
public static void generateAsFile(String content, String filePath) {
QrCodeUtil.generate(content, 100, 100, FileUtil.file(filePath));
}
public static String generateZxingAsBase64(String content, String logoPath) {
try {
QrCodeConfig config = buildConfig(content, logoPath, 100, 100);
int quietZone = 1;
if (quietZone > 4) {
quietZone = 4;
} else if (quietZone < 0) {
quietZone = 0;
}
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.ERROR_CORRECTION, config.getErrorCorrection().getBits());
hints.put(EncodeHintType.CHARACTER_SET, config.getCode());
hints.put(EncodeHintType.MARGIN, config.getMargin());
QRCode code = Encoder.encode(config.getContent(), config.getErrorCorrection(), hints);
BitMatrix bitMatrix = renderResult(code, config.getWidth(), config.getHeight(), quietZone);
BufferedImage img = toBufferedImage(config, bitMatrix);
return ImgUtil.toBase64DataUri(img, config.getPicType());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void generateZxingAsFile(String content, String filePath, String logoPath) throws IOException {
String base64 = generateZxingAsBase64(content, logoPath);
if (base64.startsWith("data:image/png;base64,")) {
base64 = base64.substring("data:image/png;base64,".length());
}
byte[] imageBytes = Base64.getDecoder().decode(base64);
try (FileOutputStream fos = new FileOutputStream(filePath)) {
fos.write(imageBytes);
fos.flush();
}
}
private static QrCodeConfig buildConfig(String content, String logo, Integer width, Integer height) {
QrCodeConfig qrConfig = new QrCodeConfig();
qrConfig.setContent(content);
qrConfig.setLogo(logo);
qrConfig.setWidth(width == null ? 200 : width);
qrConfig.setHeight(height == null ? 200 : height);
qrConfig.setPicType("png");
qrConfig.setMargin(1);
qrConfig.setErrorCorrection(ErrorCorrectionLevel.H);
qrConfig.setOnColor(MatrixToImageConfig.BLACK);
qrConfig.setOffColor(MatrixToImageConfig.WHITE);
qrConfig.setCode("UTF-8");
return qrConfig;
}
private static BitMatrix renderResult(QRCode code, int width, int height, int quietZone) {
ByteMatrix input = code.getMatrix();
if (input == null) {
throw new IllegalStateException();
}
int inputWidth = input.getWidth();
int inputHeight = input.getHeight();
int qrWidth = inputWidth + (quietZone * 2);
int qrHeight = inputHeight + (quietZone * 2);
int minSize = Math.min(width, height);
int scale = calculateScale(qrWidth, minSize);
if (scale > 0) {
int padding, tmpValue;
padding = (minSize - qrWidth * scale) / 4 * quietZone;
tmpValue = qrWidth * scale + padding;
if (width == height) {
width = tmpValue;
height = tmpValue;
} else if (width > height) {
width = width * tmpValue / height;
height = tmpValue;
} else {
height = height * tmpValue / width;
width = tmpValue;
}
}
int outputWidth = Math.max(width, qrWidth);
int outputHeight = Math.max(height, qrHeight);
int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);
int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
int topPadding = (outputHeight - (inputHeight * multiple)) / 2;
BitMatrix output = new BitMatrix(outputWidth, outputHeight);
for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
if (input.get(inputX, inputY) == 1) {
output.setRegion(outputX, outputY, multiple, multiple);
}
}
}
return output;
}
private static int calculateScale(int qrCodeSize, int expectSize) {
if (qrCodeSize >= expectSize) {
return 0;
}
int scale = expectSize / qrCodeSize;
int abs = expectSize - scale * qrCodeSize;
if (abs < expectSize * 0.15) {
return 0;
}
return scale;
}
private static BufferedImage toBufferedImage(QrCodeConfig config, BitMatrix bitMatrix) {
int qrCodeWidth = bitMatrix.getWidth();
int qrCodeHeight = bitMatrix.getHeight();
BufferedImage qrCode = new BufferedImage(qrCodeWidth, qrCodeHeight, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < qrCodeWidth; x++) {
for (int y = 0; y < qrCodeHeight; y++) {
qrCode.setRGB(x, y, bitMatrix.get(x, y) ? config.getOnColor() : config.getOffColor());
}
}
if (StrUtil.isNotBlank(config.getLogo())) {
insertLogo(qrCode, config.getLogo());
}
int realQrCodeWidth = config.getWidth();
int realQrCodeHeight = config.getHeight();
if (qrCodeWidth != realQrCodeWidth || qrCodeHeight != realQrCodeHeight) {
BufferedImage tmp = new BufferedImage(realQrCodeWidth, realQrCodeHeight, BufferedImage.TYPE_INT_RGB);
tmp.getGraphics().drawImage(qrCode.getScaledInstance(realQrCodeWidth, realQrCodeHeight, Image.SCALE_SMOOTH), 0, 0, null);
qrCode = tmp;
}
return qrCode;
}
private static void insertLogo(BufferedImage qrCode, String logo) {
BufferedImage bf = getImageByPath(logo);
if (bf != null) {
int QRCODE_WIDTH = qrCode.getWidth();
int QRCODE_HEIGHT = qrCode.getHeight();
int size = bf.getWidth() > QRCODE_WIDTH * 2 / 10 ? QRCODE_WIDTH * 2 / 50 : bf.getWidth() / 5;
bf = makeRoundBorder(bf, 60, size, Color.WHITE);
int w = Math.min(bf.getWidth(), QRCODE_WIDTH * 2 / 10);
int h = Math.min(bf.getHeight(), QRCODE_HEIGHT * 2 / 10);
Graphics2D graph = qrCode.createGraphics();
int x = (QRCODE_WIDTH - w) / 2;
int y = (QRCODE_HEIGHT - h) / 2;
graph.drawImage(bf, x, y, w, h, null);
graph.dispose();
bf.flush();
}
}
private static BufferedImage getImageByPath(String path) {
try {
if (path.startsWith("http")) {
return ImageIO.read(new URL(path));
} else {
return ImageIO.read(new File(path));
}
} catch (IOException e) {
System.out.println("logo格式或路径不正确");
return null;
}
}
private static BufferedImage makeRoundBorder(BufferedImage image, int cornerRadius, int size, Color color) {
image = makeRoundedCorner(image, cornerRadius);
int borderSize = size << 1;
int w = image.getWidth() + borderSize;
int h = image.getHeight() + borderSize;
BufferedImage output = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = output.createGraphics();
g2.setComposite(AlphaComposite.Src);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(color == null ? Color.WHITE : color);
g2.fill(new RoundRectangle2D.Float(0, 0, w, h, cornerRadius, cornerRadius));
g2.setComposite(AlphaComposite.SrcAtop);
g2.drawImage(image, size, size, null);
g2.dispose();
return output;
}
private static BufferedImage makeRoundedCorner(BufferedImage image, int cornerRadius) {
int w = image.getWidth();
int h = image.getHeight();
BufferedImage output = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = output.createGraphics();
g2.setComposite(AlphaComposite.Src);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.WHITE);
g2.fill(new RoundRectangle2D.Float(0, 0, w, h, cornerRadius, cornerRadius));
g2.setComposite(AlphaComposite.SrcAtop);
g2.drawImage(image, 0, 0, null);
g2.dispose();
return output;
}
}
QrCodeConfig
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import lombok.Data;
@Data
public class QrCodeConfig {
private String content;
private String logo;
private Integer width;
private Integer height;
private String picType;
private Integer margin;
private ErrorCorrectionLevel errorCorrection;
private Integer onColor;
private Integer offColor;
private String code;
}