网站建设滨江,做服装辅料一般什么网站找客户,百度问问我要提问,企业网站建设维护方案前几天项目上线的时候发现一个问题#xff1a;通过Hutool工具包生成的二维码在内容较少时无法填满(Margin 已设置为 0)给定大小的图片。因此导致前端在显示二维码时样式异常。 从图片中我们可以看到#xff0c;相同大小的图片#xff0c;留白内容是不一样的。其中上半部分…前几天项目上线的时候发现一个问题通过Hutool工具包生成的二维码在内容较少时无法填满(Margin 已设置为 0)给定大小的图片。因此导致前端在显示二维码时样式异常。 从图片中我们可以看到相同大小的图片留白内容是不一样的。其中上半部分的图片是一个短字符串下半部分的图片是一个长的字符串。因此基于Hutool包进行了裁边和缩放。代码如下
Maven配置
dependencygroupIdcom.google.zxing/groupIdartifactIdcore/artifactIdversion3.3.3/version
/dependency
dependencygroupIdcom.google.zxing/groupIdartifactIdjavase/artifactIdversion3.3.3/version
/dependencyQrCodeConfig.java
import com.google.zxing.EncodeHintType;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;import java.awt.*;
import java.util.HashMap;
import java.util.Map;/*** 二维吗配置信息*/
Getter
Setter
ToString
public class QrCodeConfig {/*** 塞入二维码的信息*/private String msg;/*** 生成二维码的宽*/private Integer w;/*** 生成二维码的高*/private Integer h;/*** 生成二维码的颜色*/private MatrixToImageConfig matrixToImageConfig;private MapEncodeHintType, Object hints;ToStringpublic static class QrCodeConfigBuilder {/*** The message to put into QrCode*/private String msg;/*** qrcode image width*/private Integer w;/*** qrcode image height*/private Integer h;/*** qrcode messages code, default UTF-8*/private String code;/*** 0 - 4*/private Integer padding;/*** error level, default H*/private ErrorCorrectionLevel errorCorrection;public String getMsg() {return msg;}public QrCodeConfigBuilder setMsg(String msg) {this.msg msg;return this;}public Integer getW() {return w null ? (h null ? 200 : h) : w;}public QrCodeConfigBuilder setW(Integer w) {if (w ! null w 0) {throw new IllegalArgumentException(???????????0);}this.w w;return this;}public Integer getH() {if (w ! null w 0) {throw new IllegalArgumentException(???????????0);}return h null ? (w null ? 200 : w) : h;}public QrCodeConfigBuilder setH(Integer h) {this.h h;return this;}public String getCode() {return code null ? UTF-8 : code;}public QrCodeConfigBuilder setCode(String code) {this.code code;return this;}public Integer getPadding() {if (padding null) {return 1;}if (padding 0) {return 0;}if (padding 4) {return 4;}return padding;}public QrCodeConfigBuilder setPadding(Integer padding) {this.padding padding;return this;}public ErrorCorrectionLevel getErrorCorrection() {return errorCorrection null ? ErrorCorrectionLevel.H : errorCorrection;}public QrCodeConfigBuilder setErrorCorrection(ErrorCorrectionLevel errorCorrection) {this.errorCorrection errorCorrection;return this;}private void validate() {if (msg null || msg.length() 0) {throw new IllegalArgumentException(????????????!);}}private QrCodeConfig create() {this.validate();QrCodeConfig qrCodeConfig new QrCodeConfig();qrCodeConfig.setMsg(getMsg());qrCodeConfig.setH(getH());qrCodeConfig.setW(getW());MapEncodeHintType, Object hints new HashMap(3);hints.put(EncodeHintType.ERROR_CORRECTION, this.getErrorCorrection());hints.put(EncodeHintType.CHARACTER_SET, this.getCode());hints.put(EncodeHintType.MARGIN, this.getPadding());qrCodeConfig.setHints(hints);qrCodeConfig.setMatrixToImageConfig(new MatrixToImageConfig(new Color(0, 0, 0, 255).getRGB(),new Color(0, 0, 0, 0).getRGB()));return qrCodeConfig;}/*** create qrcodeConfig** return 返回构造的 QrCodeConfig 对象*/public QrCodeConfig build() {return create();}}
}MatrixToImageUtil.java
import com.google.zxing.common.BitMatrix;
import java.awt.*;
import java.awt.image.BufferedImage;public class MatrixToImageUtil {public static BufferedImage toBufferedImage(QrCodeConfig qrCodeConfig, BitMatrix bitMatrix) {int qrCodeWidth bitMatrix.getWidth();int qrCodeHeight bitMatrix.getHeight();BufferedImage qrCode new BufferedImage(qrCodeWidth, qrCodeHeight, BufferedImage.TYPE_INT_ARGB);int onColor qrCodeConfig.getMatrixToImageConfig().getPixelOnColor();int offColor qrCodeConfig.getMatrixToImageConfig().getPixelOffColor();for (int x 0; x qrCodeWidth; x) {for (int y 0; y qrCodeHeight; y) {boolean pixelOn bitMatrix.get(x, y);int pixelColor pixelOn ? onColor : offColor;// 设置透明度int alpha pixelOn ? 255 : 0;Color color new Color(pixelColor, true);Color colorWithAlpha new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha);qrCode.setRGB(x, y, colorWithAlpha.getRGB());}}// 缩放二维码图片int realQrCodeWidth qrCodeConfig.getW();int realQrCodeHeight qrCodeConfig.getH();if (qrCodeWidth ! realQrCodeWidth || qrCodeHeight ! realQrCodeHeight) {BufferedImage tmp new BufferedImage(realQrCodeWidth, realQrCodeHeight, BufferedImage.TYPE_INT_ARGB);tmp.getGraphics().drawImage(qrCode.getScaledInstance(realQrCodeWidth, realQrCodeHeight, Image.SCALE_SMOOTH),0, 0, null);qrCode tmp;}return qrCode;}
}QrCodeGenWrapper.java
import cn.hutool.core.img.ImgUtil;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
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 lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Map;/*** 对 zxing 的 QRCodeWriter 进行扩展, 解决白边过多的问题原参考 a hrefhttps://my.oschina.net/u/566591/blog/872770.../a*/
Slf4j
public class QrCodeGenWrapper {private static final Logger logger LoggerFactory.getLogger(QrCodeGenWrapper.class);private static final int QUIET_ZONE_SIZE 4;/*** 构造 二维吗配置信息* return QrCodeConfig*/public static QrCodeConfig.QrCodeConfigBuilder createQrCodeConfig() {return new QrCodeConfig.QrCodeConfigBuilder();}/*** 生成base64格式二维吗* param content 二维吗内容* param width 宽度 默认 300* param height 高度 默认 300* param imageType 图片类型默认 png* return 返回base64格式二维码信息*/public static String generateAsBase64(String content, Integer width, Integer height, String imageType){QrCodeConfig qrConfig QrCodeGenWrapper.createQrCodeConfig().setMsg(content).setH(width null? 300 : width).setW(height null? 300 : height).setPadding(0).setErrorCorrection(ErrorCorrectionLevel.L).build();try {return ImgUtil.toBase64DataUri(asBufferedImage(qrConfig), StringUtils.isBlank(imageType)? png : imageType);} catch (Exception e) {log.error(QrCodeGenWrapper.generateAsBase64 error, e);throw new RuntimeException(QrCodeGenWrapper.generateAsBase64 生成二维码异常);}}public static BufferedImage asBufferedImage(QrCodeConfig qrCodeConfig) throws WriterException, IOException {BitMatrix bitMatrix encode(qrCodeConfig);return MatrixToImageUtil.toBufferedImage(qrCodeConfig, bitMatrix);}/*** 对 zxing 的 QRCodeWriter 进行扩展, 解决白边过多的问题* p/* 源码参考 {link com.google.zxing.qrcode.QRCodeWriter#encode(String, BarcodeFormat, int, int, Map)}*/private static BitMatrix encode(QrCodeConfig qrCodeConfig) throws WriterException {ErrorCorrectionLevel errorCorrectionLevel ErrorCorrectionLevel.L;int quietZone 1;if (qrCodeConfig.getHints() ! null) {if (qrCodeConfig.getHints().containsKey(EncodeHintType.ERROR_CORRECTION)) {errorCorrectionLevel ErrorCorrectionLevel.valueOf(qrCodeConfig.getHints().get(EncodeHintType.ERROR_CORRECTION).toString());}if (qrCodeConfig.getHints().containsKey(EncodeHintType.MARGIN)) {quietZone Integer.parseInt(qrCodeConfig.getHints().get(EncodeHintType.MARGIN).toString());}if (quietZone QUIET_ZONE_SIZE) {quietZone QUIET_ZONE_SIZE;} else if (quietZone 0) {quietZone 0;}}QRCode code Encoder.encode(qrCodeConfig.getMsg(), errorCorrectionLevel, qrCodeConfig.getHints());return renderResult(code, qrCodeConfig.getW(), qrCodeConfig.getH(), quietZone);}/*** 对 zxing 的 QRCodeWriter 进行扩展, 解决白边过多的问题* p/* 源码参考** param code {link QRCode}* param width 高* param height 宽* param quietZone 取值 [0, 4]* return {link BitMatrix}*/private static BitMatrix renderResult(QRCode code, int width, int height, int quietZone) {ByteMatrix input code.getMatrix();if (input null) {throw new IllegalStateException();}// xxx 二维码宽高相等, 即 qrWidth qrHeightint 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) {if (logger.isDebugEnabled()) {logger.debug(qrCode scale enable! scale: {}, qrSize:{}, expectSize:{}x{}, scale, qrWidth, width, height);}int padding, tmpValue;// 计算边框留白padding (minSize - qrWidth * scale) / QUIET_ZONE_SIZE * 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) {// Write the contents of this row of the barcodefor (int inputX 0, outputX leftPadding; inputX inputWidth; inputX, outputX multiple) {if (input.get(inputX, inputY) 1) {output.setRegion(outputX, outputY, multiple, multiple);}}}return output;}/*** 如果留白超过15% , 则需要缩放* (15% 可以根据实际需要进行修改)** param qrCodeSize 二维码大小* param expectSize 期望输出大小* return 返回缩放比例, 0 则表示不缩放, 否则指定缩放参数*/private static int calculateScale(int qrCodeSize, int expectSize) {if (qrCodeSize expectSize) {return 0;}int scale expectSize / qrCodeSize;int abs expectSize - scale * qrCodeSize;// 在这里配置超过多少留白则进行缩放这里已经把 0.15 改成 0 了if (abs 0) {return 0;}return scale;}
}最终效果 ---------------------------------- 只能活一次的人生当然要比谁都炽热浑浑噩噩谁也可以。 ---------------------------------