二维条码/二维码(2-dimensional bar code)是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的;在代码编制上巧妙地利用构成计算机内部逻辑基础的“0”、“1”比特流的概念,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图象输入设备或光电扫描设备自动识读以实现信息自动处理:它具有条码技术的一些共性:每种码制有其特定的字符集;每个字符占有一定的宽度;具有一定的校验功能等。同时还具有对不同行的信息自动识别功能、及处理图形旋转变化点。
1. 二维码主要特点
二维条码是一种高密度、高信息含量的便携式数据文件,是实现证件及卡片等大容量、高可靠性信息自动存储、携带并可用机器自动识读的理想手段。二维条码具有如下特点:
1、信息容量大根据不同的条空比例每平方英寸可以容纳250到1100个字符。在国际标准的证卡有效面积上(相当于信用卡面积的2/3,约500个汉字信息。这种二维条码比普通条码信息容量高几十倍,也高于磁卡。
2、编码范围广二维条码可以将照片、指纹、掌纹、签字、声音、文字等凡可数字化的信息进行编码。
3、保密、防伪性能好二维条码具有信息不可改写等多重防伪特性,它可以采用密码防伪、软件加密及利用所包含的信息如指纹、照片等进行防伪,因此具有较强的保密防伪性能。
4、译码可靠性高普通条码的译码错误率约为百分之二左右,而二维条码的误码率不超过千万分之一,译码可靠性极高。
5、修正错误能力强二维条码采用了世界上最先进的数字纠错理论,如果破损面积不超过50%,条码由于沾污、破损等所丢失的信息,可以照常被破译出。
6、容易制作且成本很低利用现有的点阵、激光、喷墨、热敏/热转印、制卡机等打印技术,即可在纸张、卡片、PVC、甚至金属表面上印出二维条码。由此所增加的费用仅是油墨的成本。
7、条码符号的形状可变同样的信息量,二维条码形状可以根据载体面积及美工设计等进行自我调整。
2. QRcode
QR(Quick-Response) code是被广泛使用的一种二维码,解码速度快。它可以存储多用类型。如下图时一个qrcode的基本结构,其中:
I.测图形、位置探测图形分隔符、定位图形:用于对二维码的定位,对每个QR码来说,位置都是固定存在的,只是大小规格会有所差异;
II.校正图形:规格确定,校正图形的数量和位置也就确定了;
III.格式信息:表示改二维码的纠错级别,分为L、M、Q、H;
IV.版本信息:即二维码的规格,QR码符号共有40种规格的矩阵(一般为黑白色),从21x21(版本1),到177x177(版本40),每一版本符号比前一版本 每边增加4个模块。
V. 数据和纠错码字:实际保存的二维码信息,和纠错码字(用于修正二维码损坏带来的错误)。
更多二维码的生成细节可戳这里
3. 带中心logo图片的二维码生成
使用先导入google开源的zxing和j2se的jar包
(1) 二维码的数据模型
package com.Lin; public class Qrcode { private String content; //二维码内容 private String filePath; //文件存放路径 private String fileName; //文件名称 private int width; //图像宽度 private int height; //图像高度 private String format; //图像类型,eg:png private int onColor = 0xFF000000; //默认为黑 private int offColor = 0xFFFFFFFF; //背景颜色,默认为白 //无参的构造函数 public Qrcode(){} public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getFilePath() { return filePath; } public void setFilePath(String filePath) { this.filePath = filePath; } public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = fileName; } public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } public String getFormat() { return format; } public void setFormat(String format) { this.format = format; } public int getOnColor() { return onColor; } public void setOnColor(int onColor) { this.onColor = onColor; } public int getOffColor() { return offColor; } public void setOffColor(int offColor) { this.offColor = offColor; } }
(2) 生成二维码图像
/** * 生成二维码图像 * @param q 二维码模型 * @param logoPath 中心logo的存储路径,为null时没中心logo * @throws WriterException * @throws IOException */ public void encode(Qrcode q, String logoPath) throws WriterException, IOException { BitMatrix bitMatrix; Hashtable<EncodeHintType, Integer> hints = new Hashtable<>(); hints.put(EncodeHintType.MARGIN, 1); //设置二维码空白边框的大小 1-4,1是最小 4是默认的国标 //生成矩阵 bitMatrix = new MultiFormatWriter().encode(new String(q.getContent().getBytes("UTF-8"),"iso-8859-1"), BarcodeFormat.QR_CODE, q.getWidth(), q.getHeight(), hints); //存储路径 Path path = FileSystems.getDefault().getPath(q.getFilePath(), q.getFileName()); //矩阵颜色设置 MatrixToImageConfig config = new MatrixToImageConfig(q.getOnColor(), q.getOffColor()); BufferedImage image = MatrixToImageWriter.toBufferedImage(bitMatrix, config); if(null == logoPath){ //不需要中心logo ImageIO.write(image, q.getFormat(), new File(path.toString())); }else{ this.overlapImage(image, path.toString(), logoPath, q.getFormat()); } System.out.println("success"); }
(3) 在二维码中心加入logo
/** * 对生成的二维码添加中心图形 * @param image * @param imgSavePath * @param logoPath * @param format */ public void overlapImage(BufferedImage image, String imgSavePath, String logoPath, String format) { try { BufferedImage logo = scale(logoPath, image.getWidth() / 5, image.getHeight() / 5, true); Graphics2D g = image.createGraphics(); //logo宽高 int width = image.getWidth() / 5; int height = image.getHeight() / 5; //logo起始位置,此目的是为logo居中显示 int x = (image.getWidth() - width) / 2; int y = (image.getHeight() - height) / 2; g.drawImage(logo, x, y, width, height, null); g.dispose(); ImageIO.write(image, format, new File(imgSavePath)); } catch (Exception e) { e.printStackTrace(); } }
(4) 将原始图像缩放至合适尺寸
/** * 把传入的原始图像按高度和宽度进行缩放,生成符合要求的图标 * @param srcImageFile 源文件地址 * @param height 目标高度 * @param width 目标宽度 * @param hasFiller 比例不对时是否需要补白:true为补白; false为不补白; * @throws IOException */ private static BufferedImage scale(String srcImageFile, int height, int width, boolean hasFiller) throws IOException { double ratio = 0.0; // 缩放比例 File file = new File(srcImageFile); BufferedImage srcImage = ImageIO.read(file); Image destImage = srcImage.getScaledInstance(width, height, BufferedImage.SCALE_SMOOTH); // 计算比例 if ((srcImage.getHeight() > height) || (srcImage.getWidth() > width)) { if (srcImage.getHeight() > srcImage.getWidth()) { ratio = (new Integer(height)).doubleValue() / srcImage.getHeight(); } else { ratio = (new Integer(width)).doubleValue() / srcImage.getWidth(); } AffineTransformOp op = new AffineTransformOp( AffineTransform.getScaleInstance(ratio, ratio), null); destImage = op.filter(srcImage, null); } if (hasFiller) {// 补白 BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D graphic = image.createGraphics(); graphic.setColor(Color.white); graphic.fillRect(0, 0, width, height); if (width == destImage.getWidth(null)) graphic.drawImage(destImage, 0, (height - destImage.getHeight(null)) / 2, destImage.getWidth(null), destImage.getHeight(null), Color.white, null); else graphic.drawImage(destImage, (width - destImage.getWidth(null)) / 2, 0, destImage.getWidth(null), destImage.getHeight(null), Color.white, null); graphic.dispose(); destImage = image; } return (BufferedImage) destImage; }
4. 二维码的解码
/** * 解析图像 * @param filePath 二维码存放路径 * @throws IOException * @throws NotFoundException */ public void decode(String filePath) throws IOException, NotFoundException { BufferedImage image; image = ImageIO.read(new File(filePath)); LuminanceSource source = new BufferedImageLuminanceSource(image); Binarizer binarizer = new HybridBinarizer(source); BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer); Map<DecodeHintType, Object> hints = new HashMap<DecodeHintType, Object>(); hints.put(DecodeHintType.CHARACTER_SET, "UTF-8"); Result result = new MultiFormatReader().decode(binaryBitmap, hints);// 对图像进行解码 System.out.println("图片中内容: "); System.out.println(result.getText()); }
5. 生成二维码的示例
package com.Lin; import java.io.IOException; import com.google.zxing.NotFoundException; import com.google.zxing.WriterException; public class Test { public static void main(String[] args) throws WriterException, IOException, NotFoundException { TestQrcode t = new TestQrcode(); //二维码数据的封装,便于以后数据与业务逻辑的分离 Qrcode q = new Qrcode(); q.setContent("我多么想和你见一面"); q.setFilePath("D://"); q.setFileName("1.png"); q.setHeight(200); q.setWidth(200); q.setFormat("png"); q.setOnColor(0xFF4169E1); //Royal blue String logoPath = "D://1.jpg"; t.encode(q, logoPath); // t.decode("D://1.png"); } }
Zxing和j2se的jar包、项目代码及应用例子在github
生成的二维码效果: