二维码也叫二维条码或二维条形码。二维码是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的;在代码编制上巧妙地利用构成计算机内部逻辑基础的“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();
}
}
```
-
引用: