使用zxing生成的二维码边框大小无法自定义问题

本文深入解析了Zxing生成二维码时边距过大的原因,揭示了源码中的计算机制,并提供了一种通过自定义边框和图片缩放解决留白问题的方法。

之前的项目用到了zxing生成二维码,发现周围的白框大的感人,使用EncodeHintType.MARGIN设置也没什么效果。今天看了一下zxing的源码,才发现其中的缘由。
先来看我原本是怎么使用zxing生成二维码的

/**
 * 生成二维码
 * @param url 二维码内容
 * @return 返回新image
 */
public static BufferedImage createQRcode(String url) throws Exception {
    int width = 300;
    int height = 300;

    Hashtable<EncodeHintType, Object> hints = new Hashtable<>();
    hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
    hints.put(EncodeHintType.MARGIN, 0);
    hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
    BitMatrix bitMatrix = new MultiFormatWriter().encode(url, BarcodeFormat.QR_CODE, width, height, hints);

    // 生成二维码
    BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++) {
            image.setRGB(x, y, bitMatrix.get(x, y) ? BLACK : WHITE);
        }
    }
    return image;
}

顺着源码点下去,最后发现是在renderResult()方法里对宽高做了一系列的处理
我们发现我们设置的hints.put(EncodeHintType.MARGIN, 0)边距并没有直接使用,而是经过一系列的计算得出了 leftPadding 与 topPadding。

private static BitMatrix renderResult(AztecCode code, int width, int height) {
    BitMatrix input;
    if ((input = code.getMatrix()) == null) {
        throw new IllegalStateException();
    } else {
        int inputWidth = input.getWidth();
        int inputHeight = input.getHeight();
        int outputWidth = Math.max(width, inputWidth);
        int outputHeight = Math.max(height, inputHeight);
        int multiple = Math.min(outputWidth / inputWidth, outputHeight / inputHeight);
        int leftPadding = (outputWidth - inputWidth * multiple) / 2;
        int topPadding = (outputHeight - inputHeight * multiple) / 2;
        BitMatrix output = new BitMatrix(outputWidth, outputHeight);
        int inputY = 0;
        for(int outputY = topPadding; inputY < inputHeight; outputY += multiple) {
            int inputX = 0;
            for(int outputX = leftPadding; inputX < inputWidth; outputX += multiple) {
                if (input.get(inputX, inputY)) {
                    output.setRegion(outputX, outputY, multiple, multiple);
                }

                ++inputX;
            }
            ++inputY;
        }
        return output;
    }
}

inputWidth ,inputHeight 是 zxing 根据我们所传的url与指定的BarcodeFormat.QR_CODE规则生成的。

比如我们传入的宽和高为250
可以看到生成的inputWidth 和 inputHeight 为33,
quietZone就是我们之前传入的1,
width,height都是之前设置的250。
multiple=7,
内容区为33x33,而我们需要的大小为250x250,33放大7倍后为231,距离250还差19。
19/2 =9
最后经过计算得出了 leftPadding 与 topPadding 都为 9。

如果此时将margin设为 4, 宽和高还是设为 250
故multiple=6,leftPading =(250 - 33*6)/2 = 26,两边就有很大的空白。
zxing会采用这种策略,是为了提高二维码的容错率,以免出现失真导致二维码无法扫描。
了解了zxing的这种计算机制后,我们就可以根据想要的边框大小来传入相应的值了。

为了解决这种留白自定义问题,我们可以采用另外一种方式——图片缩放

/**
 * 生成二维码
 * @param url 二维码内容
 * @return 返回新image
 */
public static BufferedImage createQRcode(String url) throws Exception {
	//二维码容错率,分四个等级:H、L 、M、 Q
	//生成二维码中的设置
    Hashtable hints = new Hashtable();
    //编码、容错率、二维码边框宽度,这里文档说设置0-4
    hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
    hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); 
    hints.put(EncodeHintType.MARGIN, 0);
    
	//二维码图片大小
    int size = 200;  
 	//生成bitMatrix
    BitMatrix bitMatrix = new MultiFormatWriter().encode(url, BarcodeFormat.QR_CODE, size, size,hints);

	//自定义白边边框宽度
    int margin = 5;  
    //生成新的bitMatrix
    bitMatrix = updateBit(bitMatrix, margin); 

    //因为二维码生成时,白边无法控制,去掉原有的白边,再添加自定义白边后,二维码大小与size大小就存在差异了,
    //为了让新生成的二维码大小还是size大小,根据size重新生成图片
    BufferedImage bi =  MatrixToImageWriter.toBufferedImage(bitMatrix);
    //根据size放大、缩小生成的二维码
    bi = zoomInImage(bi,size,size);
}

/**
 *因为二维码边框设置那里不起作用,不管设置多少,都会生成白边,所以根据网上
 *的例子进行修改,自定义控制白边宽度,该方法生成自定义白边框后的bitMatrix;
 */
private BitMatrix updateBit(BitMatrix matrix, int margin){
    int tempM = margin*2;
    //获取二维码图案的属性
    int[] rec = matrix.getEnclosingRectangle();   
    int resWidth = rec[2] + tempM;
    int resHeight = rec[3] + tempM;
    // 按照自定义边框生成新的BitMatrix
    BitMatrix resMatrix = new BitMatrix(resWidth, resHeight); 
    resMatrix.clear();
	//循环,将二维码图案绘制到新的bitMatrix中
	for(int i= margin; i < resWidth- margin; i++){
	    for(int j=margin; j < resHeight-margin; j++){
	        if(matrix.get(i-margin + rec[0], j-margin + rec[1])){
	            resMatrix.set(i,j);
	        }
	    }
	 }
	 return resMatrix;
 }
 /**
  * 图片放大缩小
  */
public static BufferedImage  zoomInImage(BufferedImage  originalImage, int width, int height){
    BufferedImage newImage = new BufferedImage(width,height,originalImage.getType());
    Graphics g = newImage.getGraphics();
    g.drawImage(originalImage, 0,0,width,height,null);
    g.dispose();
    return newImage;
}

借鉴的文章地址:
https://www.it610.com/article/1280736011311136768.htm
https://www.cnblogs.com/yjanb11/articles/11655194.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值