二维条码/二维码(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包、项目代码及应用例子在csdn
生成的二维码效果: