二维码概述(QRCode)

  二维码也叫二维条码或二维条形码。二维码是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的;在代码编制上巧妙地利用构成计算机内部逻辑基础的“0”、“1”比特流的概念,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图像输入设备或光电扫描设备自动识读以实现信息自动处理。

  • 二维码的起源

    二维码的起源是日本,原本是DensoWave公司为了追踪汽车零部件而设计的一种条码。

  • 二维码的特点

  1、高密度编码,信息容量大:可容纳多达l850个大写字母或2710个数字或1108个字节,或500多个汉字,比普通条码信息容量约高几十倍。

  2、编码范围广:该条码可以把图片、声音、文字、签字、指纹等可以数字化的信息进行编码,用条码表示出来;可以表示多种语言文字;可表示图像数据。

  3、容错能力强,具有纠错功能:这使得二维条码因穿孔、污损等引起局部损坏时,照样可以正确得到识读,损毁面积达50%仍可恢复信息。

  4、译码可靠性高:它比普通条码译码错误率百万分之二要低得多,误码率不超过千万分之一。

  5、可引入加密措施:保密性、防伪性好。

  6、成本低,易制作,持久耐用。

  7、条码符号形状、尺寸大小比例可变。

  8、二维条码可以使用激光或CCD阅读器识读。

  9、二维条码具有储存量大、保密性高、追踪性高、抗损性强、备援性大、成本便宜等特性,这些特性特别适用于表单、安全保密、追踪、证照、存货盘点、资料备援等方面。

  • 二维码的类型

  二维码可以分为堆叠式/行排式二维码和矩阵式二维码。它具有条码技术的一些共性:每种码制有其特定的字符集;每个字符占有一定的宽度;具有一定的校验功能等。同时还具有对不同行的信息自动识别功能、及处理图形旋转变化等特点。堆叠式/行排式二维码形态上是由多行短截的一维码堆叠而成;矩阵式二维码以矩阵的形式组成,在矩阵相应元素位置上用“点”表示二进制“1”,用“空”表示二进制“0”,“点”和“空”的排列组成代码。

  1、堆叠式/行排式

  堆叠式/行排式二维码(又称堆积式二维码或层排式二维码),编码原理是建立在一维码基础之上,按需要堆积成二行或多行。它在编码设计、校验原理、识读方式等方面继承了一维码的一些特点,识读设备与条码印刷与一维条码技术兼容。但由于行数的增加,需要对其进行判定,其译码算法与软件也不完全相同于一维条码。有代表性的行排式二维码有:Code16K、Code49、PDF417等。

  2、矩阵式

  矩阵式二维码(又称棋盘式二维码),它是在一个矩形空间通过黑、白像素在矩阵中的不同分布进行编码。在矩阵相应元素位置上,用点(方点、圆点或其他形状)的出现表示二进制“1”,点的不出现表示二进制的“0”,点的排列组合确定了矩阵式二维码所代表的意义。矩阵式二维码是建立在计算机图像处理技术、组合编码原理等基础上的一种新型图形符号自动识读处理码制。具有代表性的矩阵式二维码有:CodeOne、MaxiCode、QRCode、DataMatrix等。

  在目前几十种二维码中,常用的码制有:PDF417二维码,Datamatrix二维码,Maxicode二维码,QRCode,Code49,Code16K,Codeone等,除了这些常见的二维码之外,还有Vericode条码、CP条码、CodablockF条码、田字码、Ultracode条码,Aztec条码。

  • 二维码(QRcode)容量的计算

这里以第一版的来进行计算,第一版(21×21)不的格式如下:

输入图片说明

  其中的只有黄色的是存储数据的内容,在第一版本中只有217(21×21 - 8*9*3 -8)个存储数据的数空间,
  而其中有用的数据是 217 - 1个数据结尾标志 = 216 ,在216bit 。 
  216bit/8 = 27Byte 在版本一的QR码中只能够存储27Byte的数据(要是有校验容错就更少了)
  其他版本的QR码也是采取这样计算的。
  • 代码实现

    
        package com.zero.tree;
    
        import java.awt.Color;
        import java.awt.Graphics2D;
        import java.awt.image.BufferedImage;
        import java.io.File;
        import java.io.IOException;
        import java.io.InputStream;
        import java.io.OutputStream;
    
       import javax.imageio.ImageIO;
    
       import org.apache.commons.lang3.StringUtils;
    
       import com.swetake.util.Qrcode;
    
       import jp.sourceforge.qrcode.QRCodeDecoder;
       import jp.sourceforge.qrcode.exception.DecodingFailedException;
    
       public class QrCodeBase {
    
    	       /**
    	        * @创建人 zero
       	 * @创建时间 2018年6月1日 下午7:12:21
       	 * @说明  生成二维码(QRCode)图片
       	 * @备注
    	        * @param content 存储内容
    	        * @param imgPath 图片路径
    	        * @param imgType 图片类型
       	 * @param size 二维码尺寸
    	        * @return void
       	 * @throws Exception 
    	        */
              	public static void encoderQRCode(String content, String imgPath, String imgType,
    			int size, String charset) throws Exception {
    	       	try {
    			// 二维码级别 (1-40)
    			if (size < 1 || size > 40) {
    				// 默认为 7
    				size = 7;
    			}
    			BufferedImage bufImg = qRCodeCommon(content, size, charset);
    			File imgFile = new File(imgPath);
    			// 生成二维码QRCode图片
    			if (StringUtils.isBlank(imgType)) {
    				imgType = "png";
    			}
    			ImageIO.write(bufImg, imgType, imgFile);
    		} catch (Exception e) {
    			throw new Exception("function encoderQRCode: ", e);
    		}
    	       }
    
    	       /**
    	        * 生成二维码(QRCode)图片
    	        * @param content 存储内容
    	        * @param output 输出流
    	        * @param imgType 图片类型
    	        * @param size 二维码尺寸
    	        * @throws Exception 
    	        * 未测试呢
              	 */
       	public static void encoderQRCode(String content, OutputStream output, String imgType,
    			int size, String charset) throws Exception {
    		try {
    			BufferedImage bufImg = qRCodeCommon(content, size, charset);
    			// 生成二维码QRCode图片
    			if (StringUtils.isBlank(imgType)) {
    				imgType = "png";
    			}
    			ImageIO.write(bufImg, imgType, output);
    		} catch (Exception e) {
    			throw new Exception("function encoderQRCode: ", e);
    		}
       	}
    
       	/**
       	 * @创建人 zero
       	 * @创建时间 2018年6月1日 下午7:12:46
       	 * @说明 生成二维码(QRCode)图片的公共方法
       	 * @备注
       	 * @param content 存储内容
       	 * @param size 二维码尺寸
       	 * @param charset 字符集 -默认utf-8
       	 * @return BufferedImage
       	 * @throws Exception
       	 */
       	private static BufferedImage qRCodeCommon(String content, int size, String charset) throws Exception {
    		BufferedImage bufImg = null;
    		try {
    			Qrcode qrcodeHandler = new Qrcode();
    			// 设置二维码排错率,可选L(7%)、M(15%)、Q(25%)、H(30%),排错率越高可存储的信息越少,但对二维码清晰度的要求越小
    			qrcodeHandler.setQrcodeErrorCorrect('M');
    			qrcodeHandler.setQrcodeEncodeMode('B');
    			// 设置设置二维码尺寸,取值范围1-40,值越大尺寸越大,可存储的信息越大
    			qrcodeHandler.setQrcodeVersion(size);
    
    			// 获得内容的字节数组,设置编码格式
    			byte[] contentBytes = content.getBytes("utf-8");
    			if (!StringUtils.isBlank(charset)) {
    				contentBytes = content.getBytes(charset);
    			}
    
    			// 图片尺寸
    			int imgSize = 67 + 12 * (size - 1);
    			bufImg = new BufferedImage(imgSize, imgSize, BufferedImage.TYPE_INT_RGB);
    			Graphics2D gs = bufImg.createGraphics();
    
    			// 设置背景颜色 -> WHITE
    			gs.setBackground(Color.WHITE);
    			gs.clearRect(0, 0, imgSize, imgSize);
    			// 设定图像颜色 -> BLACK
    			gs.setColor(Color.BLACK);
    
    			// 设置偏移量,不设置可能导致解析出错
    			int pixoff = 2;
    
    			// 内容最大长度  如果内容不全增加级别 (1 - 40)
            int lengthTmp = (21 + 4*(size - 1));
    			int contentLength = (lengthTmp * lengthTmp - (8*9*3 + 8 + 1)) / 8;
    
    			// 输出内容> 二维码
    			if (contentBytes.length > 0 && contentBytes.length < contentLength) {
    			    boolean[][] codeOut = qrcodeHandler.calQrcode(contentBytes);
    				for (int i = 0; i < codeOut.length; i++) {
    					for (int j = 0; j < codeOut.length; j++) {
    						if (codeOut[j][i]) {
    							gs.fillRect(j * 3 + pixoff, i * 3 + pixoff, 3, 3);
    						}
    					}
    				}
    			} else {
    				throw new Exception("QRCode content bytes length = " 
    			            + contentBytes.length + " not in [0, " + contentLength + "].");
    			}
    
    			gs.dispose();
    			bufImg.flush();
    
    		} catch (Exception e) {
    			throw new Exception("生成二维码时异常: ", e);
    		}
    		return bufImg;
    	       }
    
    	       /**
       	 * @创建人 zero
       	 * @创建时间 2018年6月1日 下午7:37:44
       	 * @说明 解析二维码(QRCODE)图片
       	 * @备注
       	 * @param imgPath
       	 * @return String
    	        * @throws Exception 
    	        */
       	public static String decoderQRCode(String imgPath, String charset) throws Exception {
    		// QRCode 二维码图片的文件
    		File imageFile = new File(imgPath);
    		BufferedImage bufImg = null;
    		String content = null;
    		try {
    			bufImg = ImageIO.read(imageFile);
    			QRCodeDecoder decoder = new QRCodeDecoder();
    			content = new String(decoder.decode(new DimensionQRCodeImage(bufImg)), charset); 
    		} catch (IOException e) {
    			throw new Exception(e);
    		} catch (DecodingFailedException dfe) {
    			throw new Exception(dfe);
    		}
    		return content;
    	       }
    
    	       /**
       	 * 解析二维码(QRCode)
       	 * @param input 输入流
       	 * @return
       	 * 未测试
       	 */
       	public String decoderQRCode(InputStream input) {
    		BufferedImage bufImg = null;
    		String content = null;
    		try {
    			bufImg = ImageIO.read(input);
    			QRCodeDecoder decoder = new QRCodeDecoder();
    			content = new String(decoder.decode(new DimensionQRCodeImage(bufImg)), "utf-8"); 
    		} catch (IOException e) {
    			System.out.println("Error: " + e.getMessage());
    			e.printStackTrace();
    		} catch (DecodingFailedException dfe) {
    			System.out.println("Error: " + dfe.getMessage());
    			dfe.printStackTrace();
    		}
    		return content;
    	       }
    
       }
    
    

```java
   
 package com.zero.tree;

import java.awt.image.BufferedImage;

import jp.sourceforge.qrcode.data.QRCodeImage;

public class DimensionQRCodeImage implements QRCodeImage {

BufferedImage bufImg;

public DimensionQRCodeImage(BufferedImage bufImg) {
	this.bufImg = bufImg;
}

@Override
public int getHeight() {
		return bufImg.getHeight();
}

@Override
public int getPixel(int x, int y) {
		return bufImg.getRGB(x, y);
}

@Override
public int getWidth() {
		return bufImg.getWidth();
}

}
```

猜你喜欢

转载自my.oschina.net/abbchina/blog/1822801