二维码介绍

简介

二维码又称 QR Code,QR 全称 Quick Response,是一个近几年来移动设备上超流行的一种编码方式,它比传统的 Bar Code 条形码能存更多的信息,也能表示更多的数据类型:比如:字符,数字,日文,中文、网址等等。这两天学习了一下二维码图片生成的相关细节.

基础知识

首先,我们先说一下二维码一共有 40 个尺寸。官方叫版本 Version。Version 1 是 21 x 21 的矩阵,Version 2 是 25 x 25 的矩阵,Version 3 是 29 的尺寸,每增加一个 version,就会增加 4 的尺寸,公式是:(V-1)4 + 21(V是版本号) 最高 Version 40,(40-1)4+21 = 177,所以最高是 177 x 177 的正方形。

基础知识参考:http://news.cnblogs.com/n/191671/

二维码实现代码

我们使用的zxing实现的二维码生成以及二维码识别。

依赖环境

    <!-- https://mvnrepository.com/artifact/com.google.zxing/core --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.3.3</version></dependency><!-- https://mvnrepository.com/artifact/com.google.zxing/javase --><dependency><groupId>com.google.zxing</groupId><artifactId>javase</artifactId><version>3.3.3</version></dependency>

制作二维码代码

package com.blue.example;import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Shape;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.OutputStream;
import java.util.Hashtable;
import java.util.Random;
import java.util.UUID;
import javax.imageio.ImageIO;import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.Result;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;/*** @author shenjie* @version v1.0* @Description* @Date: Create in 10:25 2018/6/21* @Modifide By:**///      ┏┛ ┻━━━━━┛ ┻┓
//      ┃       ┃
//      ┃   ━   ┃
//      ┃ ┳┛   ┗┳ ┃
//      ┃       ┃
//      ┃   ┻   ┃
//      ┃       ┃
//      ┗━┓   ┏━━━┛
//        ┃   ┃   神兽保佑
//        ┃   ┃   代码无BUG!
//        ┃   ┗━━━━━━━━━┓
//        ┃           ┣┓
//        ┃             ┏┛
//        ┗━┓ ┓ ┏━━━┳ ┓ ┏━┛
//          ┃ ┫ ┫   ┃ ┫ ┫
//          ┗━┻━┛   ┗━┻━┛/*** 二维码生成工具* @author ljheee**/
public class QRCodeUtil {private static final String CHARSET = "utf-8";private static final String FORMAT_NAME = "JPG";// 二维码尺寸private static final int QRCODE_SIZE = 300;// LOGO宽度private static final int WIDTH = 60;// LOGO高度private static final int HEIGHT = 60;/*** 生成二维码* @param content   源内容* @param imgPath   生成二维码保存的路径* @param needCompress  是否要压缩* @return      返回二维码图片* @throws Exception*/private static BufferedImage createImage(String content, String imgPath, boolean needCompress) throws Exception {Hashtable hints = new Hashtable();hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);hints.put(EncodeHintType.CHARACTER_SET, CHARSET);hints.put(EncodeHintType.MARGIN, 1);BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE,hints);int width = bitMatrix.getWidth();int height = bitMatrix.getHeight();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) ? 0xFF000000 : 0xFFFFFFFF);}}if (imgPath == null || "".equals(imgPath)) {return image;}// 插入图片QRCodeUtil.insertImage(image, imgPath, needCompress);return image;}/*** 在生成的二维码中插入图片* @param source* @param imgPath* @param needCompress* @throws Exception*/private static void insertImage(BufferedImage source, String imgPath, boolean needCompress) throws Exception {File file = new File(imgPath);if (!file.exists()) {System.err.println("" + imgPath + "   该文件不存在!");return;}Image src = ImageIO.read(new File(imgPath));int width = src.getWidth(null);int height = src.getHeight(null);if (needCompress) { // 压缩LOGOif (width > WIDTH) {width = WIDTH;}if (height > HEIGHT) {height = HEIGHT;}Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH);BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);Graphics g = tag.getGraphics();g.drawImage(image, 0, 0, null); // 绘制缩小后的图g.dispose();src = image;}// 插入LOGOGraphics2D graph = source.createGraphics();int x = (QRCODE_SIZE - width) / 2;int y = (QRCODE_SIZE - height) / 2;graph.drawImage(src, x, y, width, height, null);Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);graph.setStroke(new BasicStroke(3f));graph.draw(shape);graph.dispose();}/*** 生成带logo二维码,并保存到磁盘* @param content* @param imgPath   logo图片* @param destPath* @param needCompress* @throws Exception*/public static String encode(String content, String imgPath, String destPath, boolean needCompress) throws Exception {BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress);mkdirs(destPath);String file = UUID.randomUUID().toString() + ".jpg";//生成随机文件名ImageIO.write(image, FORMAT_NAME, new File(destPath + "/" + file));return file;}public static void mkdirs(String destPath) {File file = new File(destPath);// 当文件夹不存在时,mkdirs会自动创建多层目录,区别于mkdir。(mkdir如果父目录不存在则会抛出异常)if (!file.exists() && !file.isDirectory()) {file.mkdirs();}}public static String encode(String content, String imgPath, String destPath) throws Exception {return QRCodeUtil.encode(content, imgPath, destPath, false);}public static String encode(String content, String destPath, boolean needCompress) throws Exception {return QRCodeUtil.encode(content, null, destPath, needCompress);}public static String encode(String content, String destPath) throws Exception {return QRCodeUtil.encode(content, null, destPath, false);}public static void encode(String content, String imgPath, OutputStream output, boolean needCompress)throws Exception {BufferedImage image = QRCodeUtil.createImage(content, imgPath, needCompress);ImageIO.write(image, FORMAT_NAME, output);}public static void encode(String content, OutputStream output) throws Exception {QRCodeUtil.encode(content, null, output, false);}/*** 从二维码中,解析数据* @param file  二维码图片文件* @return   返回从二维码中解析到的数据值* @throws Exception*/public static String decode(File file) throws Exception {BufferedImage image;image = ImageIO.read(file);if (image == null) {return null;}BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));Result result;Hashtable hints = new Hashtable();hints.put(DecodeHintType.CHARACTER_SET, CHARSET);result = new MultiFormatReader().decode(bitmap, hints);String resultStr = result.getText();return resultStr;}/**** @param path 二维码图片路径* @return 解析后的二维码信息* @throws Exception*/public static String decode(String path) throws Exception {return QRCodeUtil.decode(new File(path));}}

识别二维码图片

package com.blue.example;import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import com.google.zxing.LuminanceSource;/*** @author shenjie* @version v1.0* @Description* @Date: Create in 10:23 2018/6/21* @Modifide By:**///      ┏┛ ┻━━━━━┛ ┻┓
//      ┃       ┃
//      ┃   ━   ┃
//      ┃ ┳┛   ┗┳ ┃
//      ┃       ┃
//      ┃   ┻   ┃
//      ┃       ┃
//      ┗━┓   ┏━━━┛
//        ┃   ┃   神兽保佑
//        ┃   ┃   代码无BUG!
//        ┃   ┗━━━━━━━━━┓
//        ┃           ┣┓
//        ┃             ┏┛
//        ┗━┓ ┓ ┏━━━┳ ┓ ┏━┛
//          ┃ ┫ ┫   ┃ ┫ ┫
//          ┗━┻━┛   ┗━┻━┛/*** LuminanceSource这类层次结构的目的是不同的位图实现跨平台为请求获得灰度亮度值的标准接口。该接口只提供了抽象方法,* 因此可以生成和旋转创建副本。这是为了确保一个读者不修改原来的亮度源,并让它在一个未知的状态,在链中的其他读者。* https://zxing.github.io/zxing/apidocs/com/google/zxing/LuminanceSource.html* @author ljheee**/
public class BufferedImageLuminanceSource extends LuminanceSource {private final BufferedImage image;private final int left;private final int top;public BufferedImageLuminanceSource(BufferedImage image) {this(image, 0, 0, image.getWidth(), image.getHeight());}/*** 构造方法* @param image* @param left* @param top* @param width* @param height*/public BufferedImageLuminanceSource(BufferedImage image, int left, int top, int width, int height) {super(width, height);int sourceWidth = image.getWidth();int sourceHeight = image.getHeight();if (left + width > sourceWidth || top + height > sourceHeight) {throw new IllegalArgumentException("Crop rectangle does not fit within image data.");}for (int y = top; y < top + height; y++) {for (int x = left; x < left + width; x++) {if ((image.getRGB(x, y) & 0xFF000000) == 0) {image.setRGB(x, y, 0xFFFFFFFF); // = white}}}this.image = new BufferedImage(sourceWidth, sourceHeight, BufferedImage.TYPE_BYTE_GRAY);this.image.getGraphics().drawImage(image, 0, 0, null);this.left = left;this.top = top;}@Overridepublic byte[] getRow(int y, byte[] row) {//从底层平台的位图提取一行(only one row)的亮度数据值if (y < 0 || y >= getHeight()) {throw new IllegalArgumentException("Requested row is outside the image: " + y);}int width = getWidth();if (row == null || row.length < width) {row = new byte[width];}image.getRaster().getDataElements(left, top + y, width, 1, row);return row;}@Overridepublic byte[] getMatrix() {///从底层平台的位图提取亮度数据值int width = getWidth();int height = getHeight();int area = width * height;byte[] matrix = new byte[area];image.getRaster().getDataElements(left, top, width, height, matrix);return matrix;}@Overridepublic boolean isCropSupported() {//是否支持裁剪return true;}/*** 返回一个新的对象与裁剪的图像数据。实现可以保存对原始数据的引用,而不是复制。*/@Overridepublic LuminanceSource crop(int left, int top, int width, int height) {return new BufferedImageLuminanceSource(image, this.left + left, this.top + top, width, height);}@Overridepublic boolean isRotateSupported() {//是否支持旋转return true;}@Overridepublic LuminanceSource rotateCounterClockwise() {//逆时针旋转图像数据的90度,返回一个新的对象。int sourceWidth = image.getWidth();int sourceHeight = image.getHeight();AffineTransform transform = new AffineTransform(0.0, -1.0, 1.0, 0.0, 0.0, sourceWidth);BufferedImage rotatedImage = new BufferedImage(sourceHeight, sourceWidth, BufferedImage.TYPE_BYTE_GRAY);Graphics2D g = rotatedImage.createGraphics();g.drawImage(image, transform, null);g.dispose();int width = getWidth();return new BufferedImageLuminanceSource(rotatedImage, top, sourceWidth - (left + width), getHeight(), width);}
}

测试代码

package com.blue.example;import java.io.File;/*** @author shenjie* @version v1.0* @Description* @Date: Create in 10:25 2018/6/21* @Modifide By:**///      ┏┛ ┻━━━━━┛ ┻┓
//      ┃       ┃
//      ┃   ━   ┃
//      ┃ ┳┛   ┗┳ ┃
//      ┃       ┃
//      ┃   ┻   ┃
//      ┃       ┃
//      ┗━┓   ┏━━━┛
//        ┃   ┃   神兽保佑
//        ┃   ┃   代码无BUG!
//        ┃   ┗━━━━━━━━━┓
//        ┃           ┣┓
//        ┃             ┏┛
//        ┗━┓ ┓ ┏━━━┳ ┓ ┏━┛
//          ┃ ┫ ┫   ┃ ┫ ┫
//          ┗━┻━┛   ┗━┻━┛public class QRCodeApp {public static void main(String[] args) throws Exception {//生成带logo 的二维码String text = "https://www.cnblogs.com/shenjie2017";String fileName = null;fileName= QRCodeUtil.encode(text, "d:/tmp/qrcode/logo/chunchun.jpg", "d:/tmp/qrcode/img", true);//生成不带logo 的二维码String textt = "http://www.baidu.com";fileName = QRCodeUtil.encode(text,"","d:/tmp/qrcode/img",true);//指定二维码图片,解析返回数据System.out.println(QRCodeUtil.decode("d:/tmp/qrcode/img"+ File.separator+fileName));}
}

参考资料

  1. https://news.cnblogs.com/n/191671/

  2. https://www.cnblogs.com/hujunzheng/archive/2016/07/11/5661443.html

转载于:https://www.cnblogs.com/shenjie2017/articles/9214057.html

二维码原理及生成示例相关推荐

  1. 二维码原理解析,生成一个二维码需要这些知识

    /   今日科技快讯   / 近日,在英伟达取消以400亿美元收购英国芯片设计公司ARM的计划后,ARM宣布将在全球范围内裁员,大约涉及1000名员工.ARM在声明中表示:"与其他公司一样, ...

  2. Android二维码原理与优化方向

    困惑 做过Android的二维码扫描的童鞋可能会遇到过,很多二维码识别不了,或者识别速度慢.一般造成这个识别不出来的原因,大概以下几点: Android手机配置不一样,手机像素高低也有不同,有的手机还 ...

  3. 二维码原理--开胃小知识

    二维码是什么原理?不知道从什么时候开始,我们的生活突然之间就充满了二维码,看网页要扫二维码,加好友要扫二维码,现在连楼下卖草莓的大爷都支持扫码支付,那么,你有没有想过,这个长得很奇怪的二维码,到底是怎 ...

  4. Android zxing,轻松实现二维码扫描、生成

    Android  zxing,轻松实现二维码扫描.生成 一.二维码与条形码工作原理 目前的很多应用上都有扫码功能,当时微信推出二维码扫码功能时,觉得imagine,通过一张简单的图片就能扫描添加还有, ...

  5. iOS 原生二维码扫描和生成

    代码地址如下: http://www.demodashi.com/demo/12551.html 一.效果预览: 功能描述:WSLNativeScanTool是在利用原生API的条件下封装的二维码扫描 ...

  6. C# 代码生成二维码方法及代码示例(QRCoder)

    背景 二维码是越来越流行了,很多地方都有可能是使用到.如果是静态的二维码还是比较好处理的,通过在线工具就可以直接生成一张二维码图片,比如:草料二维码.但有的时候是需要动态生成的(根据动态数据生成),这 ...

  7. PHP二维码在线制作生成系统源码 无需数据库 带logo图标

    简介: PHP二维码在线制作生成系统源码 无需数据库 带logo图标 网盘下载地址: http://kekewl.net/Myae89V9DX00 图片:

  8. 二维码相关---java生成二维码名片,并且自动保存到手机通讯录中...

    二维码相关---java生成二维码名片,并且自动保存到手机通讯录中... 技术qq交流群:JavaDream:251572072 1.首先介绍一个api.   Zxing是Google提供的关于条码 ...

  9. 微信公众号二维码海报自动生成,海报通推广有绝招

    公众号二维码海报自动生成,使用公众号开发接口生成带参数的二维码和获取用户基本信息(UnionID机制)实现海报通的效果,在自己的服务器上部署就可以,也可以直接使用微号帮平台生成海报二维码. 1.生成海 ...

最新文章

  1. 软件工程概论个人作业02
  2. [Python]Python操作/管理Mysql学习(一)
  3. yum提示Error: rpmdb open failed
  4. Anaconda 安装 TensorFlow ImportError:DLL加载失败,错误代码为-1073741795
  5. Java 基础复习实践 --- Hashcode Equals
  6. (急聘) 高级软件工程师(C#.Net/JS) http://job.cnblogs.com/offer/8966/
  7. 数据库连接池的设计思路及java实现
  8. jquery-样式的添加删除操作
  9. 微信小程序保存canvas绘制的图片到本地,拒绝图片授权后继续授权
  10. python访问注册表_Python 操作注册表
  11. Android开发BroadcastReceiver广播的使用
  12. 阳新一中2021高考成绩查询,阳新一中2019高考成绩喜报、一本二本上线人数情况...
  13. asp.net 2.0中实现防盗链
  14. 【Cesium】Cesium三维模型调整
  15. 2012-7-19可樂词汇积累#9316;
  16. WGS(重测序)分析详解与脚本
  17. vue全套笔记,前端工程化+vue指令+脚手架
  18. 鹤岗市全国计算机等级考试,2020年3月黑龙江省鹤岗市计算机等级考试考务通知...
  19. 外边距塌陷原因和解决方式
  20. 智云通CRM:当客户上来就问价格,销售该怎么回答?

热门文章

  1. 嵌入式Linux 串口编程系列1——基本知识、termios结构体
  2. C语言——报数问题:有n个人围成一圈,顺序排号。从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号的那位。
  3. 内存管理单元(MMU)介绍
  4. 解决AD中PCB芯片引脚之间间距太小
  5. UI设计中色彩搭配使用技巧
  6. Ntrip通讯协议1.0
  7. 【matlab】spm数据处理
  8. 一篇文章读懂少儿机器人编程课程学什么?
  9. 体育视野杂志体育视野杂志社体育视野编辑部2022年第5期目录
  10. 09-长调用与短调用