基本使用
首先工程中导入zxing的依赖,如下:
<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.example.demo.utils;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Random;
import javax.imageio.ImageIO;
import org.springframework.util.StringUtils;
import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.oned.Code128Writer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
/**
* 类描述: 二维码生成工具类
*
* @author jianweiwang
* @version v1.0
* @link wjw@choicesoft.com.cn
* @date 2019/4/1 11:00
**/
public class BarcodeUtils {
private static final String CHARSET = "utf-8";
private static final String FORMAT = "JPG";
/**
* 条形码宽度
*/
private static final int WIDTH = 220;
/**
* 条形码高度
*/
private static final int HEIGHT = 80;
/**
* 字体大小
*/
private static final int FONT_SIZE = 28;
/**
* 加文字 条形码
*/
private static final int WORD_HEIGHT = HEIGHT + FONT_SIZE;
/**
* 二维码尺寸
*/
private static final int QR_CODE_SIZE = 300;
/**
* LOGO宽度
*/
private static final int LOGO_WIDTH = 60;
/**
* LOGO高度
*/
private static final int LOGO_HEIGHT = 60;
/**
* 设置 条形码参数
*/
private static Map<EncodeHintType, Object> hints = new HashMap<>(5);
static {
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
hints.put(EncodeHintType.MARGIN, 1);
}
/**
* 生成 图片缓冲
*
* @param vaNumber VA 码
* @return 返回BufferedImage
* @author fxbin
*/
public static BufferedImage getBarCode(String vaNumber) {
try {
Code128Writer writer = new Code128Writer();
// 编码内容, 编码类型, 宽度, 高度, 设置参数
BitMatrix bitMatrix = writer.encode(vaNumber, BarcodeFormat.CODE_128, WIDTH, HEIGHT, hints);
return MatrixToImageWriter.toBufferedImage(bitMatrix);
} catch (WriterException e) {
e.printStackTrace();
}
return null;
}
/**
* 把带logo的二维码下面加上文字
*
* @param image 条形码图片
* @param words 文字
* @return 返回BufferedImage
* @author fxbin
*/
public static BufferedImage insertWords(BufferedImage image, String words) {
// 新的图片,把带logo的二维码下面加上文字
if (!StringUtils.isEmpty(words)) {
BufferedImage outImage = new BufferedImage(WIDTH, WORD_HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = outImage.createGraphics();
// 抗锯齿
setGraphics2D(g2d);
// 设置白色
setColorWhite(g2d);
// 画条形码到新的面板
g2d.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), null);
// 画文字到新的面板
g2d.setColor(Color.BLACK);
// 字体、字型、字号
g2d.setFont(new Font("黑体", Font.PLAIN, FONT_SIZE));
//设置条形码下面的文本的纵坐标
int wordStartY = HEIGHT + 21;
//-------------------------计算条形码的左边距,即左边空白的距离------------------------
String content=words.substring(1,words.length()-1);
int inputWidth = new Code128Writer().encode(content).length;
int fullWidth = inputWidth + 1;
int outputWidth = Math.max(WIDTH, fullWidth);
int multiple = outputWidth / fullWidth;
int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
int x = leftPadding;
//-----------------------计算条形码的左边距,即左边空白的距离----------------------
String[] split = words.split("");
int len = split.length;
int step = new Long(Math.round(WIDTH - leftPadding * 2) / words.length()).intValue();
for (int i = 0; i < len; i++) {
g2d.drawString(split[i], x, wordStartY);
x = x + step;
}
outImage.flush();
return outImage;
}
return null;
}
/**
* 设置 Graphics2D 属性 (抗锯齿)
*
* @param g2d Graphics2D提供对几何形状、坐标转换、颜色管理和文本布局更为复杂的控制
*/
private static void setGraphics2D(Graphics2D g2d) {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT);
Stroke s = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER);
g2d.setStroke(s);
}
/**
* 设置背景为白色
*
* @param g2d Graphics2D提供对几何形状、坐标转换、颜色管理和文本布局更为复杂的控制
*/
private static void setColorWhite(Graphics2D g2d) {
g2d.setColor(Color.WHITE);
// 填充整个屏幕
g2d.fillRect(0, 0, 600, 600);
// 设置笔刷
g2d.setColor(Color.BLACK);
}
private static BufferedImage createImage(String content, String logoPath, boolean needCompress) throws Exception {
BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QR_CODE_SIZE, QR_CODE_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 (logoPath == null || "".equals(logoPath)) {
return image;
}
// 插入图片
BarcodeUtils.insertImage(image, logoPath, needCompress);
return image;
}
/**
* 插入LOGO
*
* @param source 二维码图片
* @param logoPath LOGO图片地址
* @param needCompress 是否压缩
* @throws Exception
*/
private static void insertImage(BufferedImage source, String logoPath, boolean needCompress) throws Exception {
File file = new File(logoPath);
if (!file.exists()) {
throw new Exception("logo file not found.");
}
Image src = ImageIO.read(new File(logoPath));
int width = src.getWidth(null);
int height = src.getHeight(null);
// 是否压缩LOGO
if (needCompress) {
if (width > LOGO_WIDTH) {
width = LOGO_WIDTH;
}
if (height > LOGO_HEIGHT) {
height = LOGO_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;
}
// 插入LOGO
Graphics2D graph = source.createGraphics();
int x = (QR_CODE_SIZE - width) / 2;
int y = (QR_CODE_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 logoPath LOGO地址
* @param destPath 存放目录
* @param needCompress 是否压缩LOGO
*/
private static String encode(String content, String logoPath, String destPath, boolean needCompress)
throws Exception {
BufferedImage image = BarcodeUtils.createImage(content, logoPath, needCompress);
mkdir(destPath);
String fileName = new Random().nextInt(99999999) + "." + FORMAT.toLowerCase();
ImageIO.write(image, FORMAT, new File(destPath + "/" + fileName));
return fileName;
}
/**
* 生成二维码(内嵌LOGO) 调用者指定二维码文件名
*
* @param content 内容
* @param logoPath LOGO地址
* @param destPath 存放目录
* @param fileName 二维码文件名
* @param needCompress 是否压缩LOGO
* @throws Exception
*/
private static String encode(String content, String logoPath, String destPath, String fileName,
boolean needCompress) throws Exception {
BufferedImage image = BarcodeUtils.createImage(content, logoPath, needCompress);
mkdir(destPath);
fileName = fileName.substring(0, fileName.indexOf(".") > 0 ? fileName.indexOf(".") : fileName.length()) + "."
+ FORMAT.toLowerCase();
ImageIO.write(image, FORMAT, new File(destPath + "/" + fileName));
return fileName;
}
/**
* 生成二维码(内嵌LOGO)
*
* @param content 内容
* @param logoPath LOGO地址
* @param destPath 存储地址
*/
public static String encode(String content, String logoPath, String destPath) throws Exception {
return BarcodeUtils.encode(content, logoPath, destPath, false);
}
/**
* 生成二维码
*
* @param content 内容
* @param destPath 存储地址
* @throws Exception
*/
public static String encode(String content, String destPath) throws Exception {
return BarcodeUtils.encode(content, null, destPath, false);
}
/**
* 生成二维码(内嵌LOGO)
*
* @param content 内容
* @param logoPath LOGO地址
* @param output 输出流
* @param needCompress 是否压缩LOGO
*/
private static void encode(String content, String logoPath, OutputStream output, boolean needCompress)
throws Exception {
BufferedImage image = BarcodeUtils.createImage(content, logoPath, needCompress);
ImageIO.write(image, FORMAT, output);
}
/**
* 生成二维码
*
* @param content 内容
* @param output 输出流
*/
public static void encode(String content, OutputStream output) throws Exception {
BarcodeUtils.encode(content, null, output, false);
}
/**
* 当文件夹不存在时,mkdirs会自动创建多层目录,区别于mkdir. (mkdir如果父目录不存在则会抛出异常)
*
* @param destPath 存放目录
*/
private static void mkdir(String destPath) {
File file = new File(destPath);
if (!file.exists() && !file.isDirectory()) {
file.mkdirs();
}
}
/**
* 解析二维码
*
* @param file 二维码图片
*/
private 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<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>();
hints.put(DecodeHintType.CHARACTER_SET, CHARSET);
result = new MultiFormatReader().decode(bitmap, hints);
return result.getText();
}
/**
* 解析二维码
*
* @param path 二维码图片地址
*/
public static String decode(String path) throws Exception {
return BarcodeUtils.decode(new File(path));
}
public static void main(String[] args) throws Exception {
// 条形码
BufferedImage image = insertWords(getBarCode("89607"), "*89607*");
assert image != null;
ImageIO.write(image, "jpg", new File("barcode.jpg"));
// 二维码
// String text = "http://localhost:8083/communication/distribution?qrid=54999";
// 不含Logo
// BarcodeUtils.encode(text, null, "C:\\\\Users\\\\ThinkPad\\\\Pictures", true);
// 含Logo,不指定二维码图片名
// BarcodeUtils.encode(text, "e:\\csdn.jpg", "e:\\", true);
// 含Logo,指定二维码图片名
// BarcodeUtils.encode(text, "C:\\Users\\ThinkPad\\Pictures\\mail-unread.png",
// "C:\\Users\\ThinkPad\\Pictures", "qrcode", true);
}
}
注意点
如果想要设置条形码周围的空白区域大小,需要通过参数如下:
/**
* hints 设置 条形码参数
*/
private static Map<EncodeHintType, Object> hints = new HashMap<>(5);
static {
//设置留白大小
hints.put(EncodeHintType.MARGIN, 1);
}
public static BufferedImage getBarCode(String vaNumber) {
try {
Code128Writer writer = new Code128Writer();
// 编码内容, 编码类型, 宽度, 高度, 设置参数
BitMatrix bitMatrix = writer.encode(vaNumber, BarcodeFormat.CODE_128, WIDTH, HEIGHT, hints);
return MatrixToImageWriter.toBufferedImage(bitMatrix);
} catch (WriterException e) {
e.printStackTrace();
}
return null;
}
但是这里有一坑,认为设置了留白大小之后,但是往往留白的大小会出乎意料。
下面通过分析zxing工具中的源码如下:
通过上述源码分析可知,如果想要设置条形码下方的展示内容与条形码两端对齐,需要知道变量leftPadding(条形码左边距的值),简单代码如下:
public static BufferedImage insertWords(BufferedImage image, String words) {
// 新的图片,把带logo的二维码下面加上文字
if (!StringUtils.isEmpty(words)) {
BufferedImage outImage = new BufferedImage(WIDTH, WORD_HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = outImage.createGraphics();
// 抗锯齿
setGraphics2D(g2d);
// 设置白色
setColorWhite(g2d);
// 画条形码到新的面板
g2d.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), null);
// 画文字到新的面板
g2d.setColor(Color.BLACK);
// 字体、字型、字号
g2d.setFont(new Font("黑体", Font.PLAIN, FONT_SIZE));
int wordStartY = HEIGHT + 21;
String content=words.substring(1,words.length()-1);
int inputWidth = new Code128Writer().encode(content).length;
int fullWidth = inputWidth + 1;
int outputWidth = Math.max(WIDTH, fullWidth);
int multiple = outputWidth / fullWidth;
int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
int x = leftPadding;
String[] split = words.split("");
int len = split.length;
int step = new Long(Math.round(WIDTH - leftPadding * 2) / words.length()).intValue();
for (int i = 0; i < len; i++) {
g2d.drawString(split[i], x, wordStartY);
x = x + step;
}
outImage.flush();
return outImage;
}
return null;
}
演示效果大致如下图所示