筆者日常: 第一次用markdown寫文章,有點手生~
聲明: 此工具類由本人二次整理改造後分享給大家,原編寫者未知,雖然本人重寫了大部分邏輯代碼,但是核心部分仍然是採用的原來的代碼。若涉及侵權問題,請隨時聯繫筆者。
工具類使用示例:
-
生成二維碼:
- 方式一:
執行方法:
/** * 生成二維碼 * * @date 2019/9/10 10:39 */ public static String test1() throws IOException, WriterException { return QrCodeUtil.encodeQrCode("螞蟻牙~嘿嘿", 500, 500, "C:\\Users\\JustryDeng\\Desktop\\abc\\二維碼(通過方式一生成).jpg"); }
生成效果如圖:
- 方式二:
執行方法:
/** * 生成二維碼 * * @date 2019/9/10 10:39 */ public static String test2() throws IOException, WriterException { return QrCodeUtil.encodeQrCodeAnotherWay("螞蟻~~~", 500, 500, "C:\\Users\\JustryDeng\\Desktop\\abc\\二維碼(通過方式二生成).jpg"); }
生成效果如圖:
- 方式一:
-
生成帶圖片的二維碼:
執行方法:
/** * 生成帶圖片的二維碼 * * @date 2019/9/10 10:39 */ public static String test3() throws IOException, WriterException { return QrCodeUtil.encodeQrCodeWithEmbeddedImg("我是二維碼代表的數據內容", 500, 500, "C:\\Users\\JustryDeng\\Desktop\\1.png", "C:\\Users\\JustryDeng\\Desktop\\abc\\帶圖片的二維碼.jpg"); }
生成效果如圖:
-
生成帶文字、帶圖片的二維碼:
執行方法:
/** * 生成帶文字、帶圖片的二維碼 * * @date 2019/9/10 10:39 */ public static String test4() throws IOException, WriterException { QrImage para = QrImage.builder() .qrCodeFileOutputPath("C:\\Users\\JustryDeng\\Desktop\\abc\\帶文字帶圖片的二維碼.jpg") .qrCodeContent("http://www.baidu.com") .qrCodeWidth(500) .qrCodeHeight(500) .embeddedImgFilePath("C:\\Users\\JustryDeng\\Desktop\\1.png") .wordContent("我的二維碼") .wordSize(20) .build(); return QrCodeUtil.encodeQrCodeWithEmbeddedImgAndFonts(para); }
生成效果如圖:
-
生成帶文字、不帶圖片的二維碼:
執行方法:
/** * 生成帶文字、不帶圖片的二維碼 * * @date 2019/9/10 10:39 */ public static String test5() throws IOException, WriterException { QrImage para = QrImage.builder() .qrCodeFileOutputPath("C:\\Users\\JustryDeng\\Desktop\\abc\\帶文字不帶圖片的二維碼.jpg") .qrCodeContent("二維碼代表的數據內容~") .qrCodeWidth(500) .qrCodeHeight(500) .wordContent("我是文字") .wordSize(20) .build(); return QrCodeUtil.encodeQrCodeWithEmbeddedImgAndFonts(para); }
生成效果如圖:
-
識別二維碼:
main方法:
被調用方法(兩種方式均可):
/** * 識別二維碼 * * @date 2019/9/10 10:39 */ public static String test6() throws IOException, NotFoundException { return QrCodeUtil.decodeQrCode( new File("C:\\Users\\JustryDeng\\Desktop\\abc\\二維碼(通過方式一生成).jpg") ); } /** * 識別二維碼 * * @date 2019/9/10 10:39 */ public static String test7() throws IOException, NotFoundException { return QrCodeUtil.decodeQrCode( new FileInputStream("C:\\Users\\JustryDeng\\Desktop\\abc\\帶文字帶圖片的二維碼.jpg") ); }
生成效果如圖:
二維碼工具類,使用示例完畢!
軟硬件環境說明: Windows10、IntelliJ IDEA、SpringBoot 2.1.8.RELEASE。
生成二維碼、識別二維碼的工具類:
<!-- https://mvnrepository.com/artifact/com.google.zxing/core -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.4.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.zxing/javase -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.4.0</version>
</dependency>
注:除了上述依賴外,本人還引入了lombok,完整的pom.xml是這樣的:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.szlaozicl</groupId>
<artifactId>zxing-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>zxing-demo</name>
<description>使用zxing生成、識別二維碼</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.zxing/core -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.4.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.zxing/javase -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
<version>3.4.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
import lombok.*;
/**
* 二維碼圖片對象
*
* @author JustryDeng
* @date 2019/9/9 15:22
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class QrImage {
/** 二維碼的內容(非空) */
private String qrCodeContent;
/** 二維碼的寬度(非空) */
private Integer qrCodeWidth;
/** 二維碼的高度(非空) */
private Integer qrCodeHeight;
/** 二維碼內嵌圖片的文件路徑(爲空則表示:二維碼中間不嵌套圖片) */
private String embeddedImgFilePath;
/** 文字的大小(即:正方形文字的長度、寬度)(非空) */
private Integer wordSize;
/** 文字的內容(非空) */
private String wordContent;
/** 二維碼文件的輸出路徑(非空) */
private String qrCodeFileOutputPath;
}
import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.szlaozicl.zxingdemo.model.QrImage;
import lombok.extern.slf4j.Slf4j;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
/**
* 二維碼 工具類
* <p>
* 注:此工具類, 是本人整理,但不是本人編寫。
* <p>
* 說明: 二維碼是一種編碼形式,
* 將內容encode,得到二維碼;,
* 將內容decode,得到數據內容。
*
* @author JustryDeng
* @date 2019/9/9 15:11
*/
@Slf4j
@SuppressWarnings("unused")
public class QrCodeUtil {
/**
* 嵌入的圖片(被程序處理後)的高度(px)
* <p>
* 注:內嵌圖片不應占據遮擋二維碼太多,否者可能導致生成的二維碼無法識別。
* 建議內嵌圖片的尺寸設置爲二維碼尺寸的1/5 或 1/4
*/
private static int embeddedImgDefaultWidth;
/** 嵌入的圖片(被程序處理後)的高度(px) */
private static int embeddedImgDefaultHeight;
/** 嵌入的圖片與二維碼圖片之間的框的寬度(px) */
private static int frameWidth;
/** 嵌入的圖片與二維碼圖片之間的框的顏色. */
private static int frameWidthColor;
/** 二維碼 碼的顏色 */
private static int qrCodeColor;
/** 二維碼 背景的顏色 */
private static int qrCodeBackgroundColor;
/** 圖片縮小後,對那些"空"下的區域補色 */
private static int fillColor;
/** 二維碼寫碼器 */
private static MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
/*
* 初始化基本參數
*/
static {
QrCodeUtil.embeddedImgDefaultWidth = 80;
QrCodeUtil.embeddedImgDefaultHeight = 80;
QrCodeUtil.frameWidth = 0;
QrCodeUtil.frameWidthColor = Color.BLUE.getRGB();
QrCodeUtil.qrCodeColor = Color.BLACK.getRGB();
QrCodeUtil.qrCodeBackgroundColor = Color.WHITE.getRGB();
QrCodeUtil.fillColor = Color.RED.getRGB();
}
/**
* 設置修改基本參數的參數值
*
* @param embeddedImgDefaultWidth
* 嵌入的圖片(被程序處理後)的高度(px)
* @param embeddedImgDefaultHeight
* 嵌入的圖片(被程序處理後)的高度(px)
* @param frameWidth
* 嵌入的圖片與二維碼圖片之間的框的寬度(px)
* @param frameWidthColor
* 嵌入的圖片與二維碼圖片之間的框的顏色
* @param qrCodeColor
* 二維碼 碼的顏色
* @param qrCodeBackgroundColor
* 二維碼 背景的顏色
* @param fillColor
* 圖片縮小後,對那些"空"下的區域補色
* @date 2019/9/9 22:47
*/
public static void modifyBasicParamsValues(Integer embeddedImgDefaultWidth,
Integer embeddedImgDefaultHeight,
Integer frameWidth,
Color frameWidthColor,
Color qrCodeColor,
Color qrCodeBackgroundColor,
Color fillColor) {
if (embeddedImgDefaultWidth != null) {
QrCodeUtil.embeddedImgDefaultWidth = embeddedImgDefaultWidth;
}
if (embeddedImgDefaultHeight != null) {
QrCodeUtil.embeddedImgDefaultHeight = embeddedImgDefaultHeight;
}
if (frameWidth != null) {
QrCodeUtil.frameWidth = frameWidth;
}
if (frameWidthColor != null) {
QrCodeUtil.frameWidthColor = frameWidthColor.getRGB();
}
if (qrCodeColor != null) {
QrCodeUtil.qrCodeColor = qrCodeColor.getRGB();
}
if (qrCodeBackgroundColor != null) {
QrCodeUtil.qrCodeBackgroundColor = qrCodeBackgroundColor.getRGB();
}
if (fillColor != null) {
QrCodeUtil.fillColor = fillColor.getRGB();
}
}
/**
* 生成二維碼(到destImagePath指向的File) --- 方式一
*
* @param content
* 二維碼的內容
* @param width
* 二維碼的寬度(px)
* @param height
* 二維碼的高度(px)
* @param destImagePath
* 生成二維碼圖片的地址
*
* @return 生成的二維碼文件path
* @throws IOException IOException
* @throws WriterException WriterException
*
* @date 2019/9/9 16:43
*/
public static String encodeQrCode(String content, int width, int height, String destImagePath)
throws IOException, WriterException {
File dest = getFile(destImagePath);
// 圖像類型
String format = "jpg";
Map<EncodeHintType, Object> hints = new HashMap<>(4);
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
try (FileOutputStream output = new FileOutputStream(dest)) {
// 生成矩陣
BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE,
width, height, hints);
// 輸出圖像
MatrixToImageWriter.writeToStream(bitMatrix, format, output);
}
return destImagePath;
}
/**
* 生成二維碼(到destImagePath指向的File) --- 方式二
*
* @param content
* 二維碼的內容
* @param width
* 二維碼的寬度(px)
* @param height
* 二維碼的高度(px)
* @param destImagePath
* 生成二維碼圖片的地址
*
* @return 生成的二維碼文件path
* @throws IOException IOException
* @throws WriterException WriterException
*
* @date 2019/9/9 16:43
*/
public static String encodeQrCodeAnotherWay(String content, int width, int height, String destImagePath)
throws IOException, WriterException {
boolean result = ImageIO.write(genBufferedImage(content, width, height), "jpg", getFile(destImagePath));
log.info(" generate Qr code file {} {}", destImagePath, result ? "success" : "fail");
return destImagePath;
}
/**
* 生成帶圖片的二維碼(到destImagePath指向的File)
*
* @param content
* 二維碼的內容
* @param width
* 二維碼的寬度(px)
* @param height
* 二維碼的高度(px)
* @param embeddedImgPath
* 被鑲嵌的圖片的地址
* @param destImagePath
* 生成二維碼圖片的地址
*
* @return 生成的二維碼文件path
* @throws IOException IOException
* @throws WriterException WriterException
*
* @date 2019/9/9 15:13
*/
public static String encodeQrCodeWithEmbeddedImg(String content, int width, int height,
String embeddedImgPath, String destImagePath)
throws IOException, WriterException {
boolean result = ImageIO.write(
genBufferedImageWithEmbeddedImg(content, width, height, embeddedImgPath),
"jpg",
getFile(destImagePath));
log.info(" generate Qr code file {} {}", destImagePath, result ? "success" : "fail");
return destImagePath;
}
/**
* 生成帶圖片的二維碼(到outputStream流)
*
* @param content
* 二維碼的內容
* @param width
* 二維碼的寬度(px)
* @param height
* 二維碼的高度(px)
* @param embeddedImgPath
* 被鑲嵌的圖片的地址
* 注:被鑲嵌的圖片,如果尺寸
*
* @throws IOException IOException
* @throws WriterException WriterException
*
* @date 2019/9/9 16:43
*/
public static void encodeQrCodeWithEmbeddedImg(String content, int width, int height,
String embeddedImgPath, OutputStream outputStream)
throws IOException, WriterException {
boolean result = ImageIO.write(
genBufferedImageWithEmbeddedImg(content, width, height, embeddedImgPath),
"jpg",
outputStream);
log.info(" generate Qr code file to OutputStream {}", result ? "success" : "fail");
}
/**
* 生成上方帶內嵌圖片、帶文字的二維碼
* <p>
* 注:若 嵌入的圖片的參數值爲null 或者爲 空的字符,那麼 只生成帶文字的二維碼
*
* @param param
* 參數模型
*
* @return 生成的二維碼文件path
* @throws IOException IOException
* @throws WriterException WriterException
*
* @date 2019/9/10 0:11
*/
public static String encodeQrCodeWithEmbeddedImgAndFonts(QrImage param)
throws IOException, WriterException {
// 首先生成二維碼圖片
BufferedImage qrImage;
String embeddedImgFilePath = param.getEmbeddedImgFilePath();
String qrCodeContent = param.getQrCodeContent();
Integer qrCodeWidth = param.getQrCodeWidth();
Integer qrCodeHeight = param.getQrCodeHeight();
if (embeddedImgFilePath == null || embeddedImgFilePath.trim().length() == 0) {
qrImage = genBufferedImage(qrCodeContent, qrCodeWidth, qrCodeHeight);
} else {
qrImage = genBufferedImageWithEmbeddedImg(qrCodeContent, qrCodeWidth,
qrCodeHeight, embeddedImgFilePath);
}
int qrCodeImageWidth = qrImage.getWidth();
int qrCodeImageHeight = qrImage.getHeight();
String[] splitStrLines;
splitStrLines = splitStrLines(param.getWordContent(), qrCodeImageWidth / param.getWordSize());
// 文字圖片的高度 = 文字行數 * 每行高度(文字高度) + 10px;
// 爲了防止 文字圖片 下面部分顯示不全, 特意在這裏額外加10像素的高度。
int fontsImageHeight = splitStrLines.length * param.getWordSize() + 10;
// 創建頂部文字的圖片
BufferedImage imageWithFontsBufferedImage = new BufferedImage(qrCodeImageWidth,
fontsImageHeight, BufferedImage.TYPE_4BYTE_ABGR);
Graphics fontsImageGraphics = imageWithFontsBufferedImage.getGraphics();
fontsImageGraphics.fillRect(0, 0, qrCodeImageWidth, fontsImageHeight);
fontsImageGraphics.setColor(Color.black);
fontsImageGraphics.setFont(new Font("宋體", Font.PLAIN, param.getWordSize()));
// 文字長度大於一行的長度,進行分行
// 每行限制的文字個數
if (param.getWordContent().length() > qrCodeImageWidth / param.getWordSize()) {
for (int i = 0; i < splitStrLines.length; i++) {
fontsImageGraphics.drawString(splitStrLines[i], 0, param.getWordSize() * (i + 1));
}
} else {
fontsImageGraphics.drawString(
param.getWordContent(),
// 總長度減去字長度除以2爲向右偏移長度
((qrCodeImageWidth / param.getWordSize() - param.getWordContent().length()) / 2) * param.getWordSize(),
param.getWordSize());
}
// 從圖片中讀取RGB
int[] imageArrayFonts = new int[qrCodeImageWidth * fontsImageHeight];
imageArrayFonts = imageWithFontsBufferedImage.getRGB(0, 0, qrCodeImageWidth,
fontsImageHeight, imageArrayFonts, 0, qrCodeImageWidth);
int[] imageArrayQr = new int[qrCodeImageWidth * qrCodeImageHeight];
imageArrayQr = qrImage.getRGB(0, 0, qrCodeImageWidth, qrCodeImageHeight,
imageArrayQr, 0, qrCodeImageWidth);
// 生成新圖片(在setsetRGB時,通過控制圖像的startX與startY, 可達到不同的拼接效果)
BufferedImage newBufferedImage = new BufferedImage(qrCodeImageWidth,
qrCodeImageHeight + fontsImageHeight, BufferedImage.TYPE_INT_RGB);
// 圖片在上, 文字在下
// 設置上半部分的RGB
newBufferedImage.setRGB(0, 0, qrCodeImageWidth, qrCodeImageHeight,
imageArrayQr, 0, qrCodeImageWidth);
// 設置下半部分的RGB
newBufferedImage.setRGB(0, qrCodeImageHeight, qrCodeImageWidth,
fontsImageHeight, imageArrayFonts, 0, qrCodeImageWidth);
// 文字在上,圖片在下
// // 設置上半部分的RGB
/// newBufferedImage.setRGB(0, 0, qrCodeImageWidth, fontsImageHeight, imageArrayFonts, 0, qrCodeImageWidth);
// // 設置下半部分的RGB
/// newBufferedImage.setRGB(0, fontsImageHeight, qrCodeImageWidth, qrCodeImageHeight, imageArrayQr, 0, qrCodeImageWidth);
String qrCodeFileOutputPath = param.getQrCodeFileOutputPath();
File outFile = getFile(qrCodeFileOutputPath);
boolean result = ImageIO.write(newBufferedImage, "jpg", outFile);
log.info(" generate Qr code file {} {}", qrCodeFileOutputPath, result ? "success" : "fail");
return qrCodeFileOutputPath;
}
/**
* 識別二維碼內容信息
*
* @param file 二維碼圖片文件
*
* @return 二維碼內容
* @throws NotFoundException NotFoundException
* @throws IOException IOException
* @date 2019/9/10 1:59
*/
public static String decodeQrCode(File file) throws NotFoundException, IOException {
BufferedImage image;
image = ImageIO.read(file);
if (image == null) {
return null;
}
String data = decodeQrCode(image);
log.info(" Qr code from [{}] data is -> {}", file.getAbsolutePath(), data);
return data;
}
/**
* 識別二維碼內容信息
*
* @param is 二維碼圖片文件流
*
* @return 二維碼內容
* @throws NotFoundException NotFoundException
* @throws IOException IOException
* @date 2019/9/10 1:59
*/
public static String decodeQrCode(InputStream is) throws NotFoundException, IOException {
BufferedImage image;
image = ImageIO.read(is);
if (image == null) {
return null;
}
String data = decodeQrCode(image);
log.info(" Qr code from InputStream data is -> {}", data);
return data;
}
/// --------------------------------------------------------以下爲輔助方法、輔助類
/**
* 獲取文件(順帶創建文件夾,如果需要的話)
*
* @param filePath
* 文件path
* @return 文件對象
* @date 2019/9/10 10:48
*/
private static File getFile(String filePath) {
File file = new File(filePath);
if (!file.getParentFile().exists()) {
boolean result = file.getParentFile().mkdirs();
log.info(" create directory {} {}", file.getParent(), result);
}
return file;
}
/**
* 識別二維碼內容信息
*
* @param image 二維碼圖片信息BufferedImage
*
* @return 二維碼內容
* @throws NotFoundException NotFoundException
* @date 2019/9/10 1:59
*/
private static String decodeQrCode(BufferedImage image) throws NotFoundException {
BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Result result;
HashMap<DecodeHintType, Object> hints = new HashMap<>(4);
hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
result = new MultiFormatReader().decode(bitmap, hints);
return result.getText();
}
/**
* 生成二維碼圖片信息BufferedImage
* <p>
* 注:BufferedImage即爲二維碼圖片的數據容器,可以利用其進一步生成二維碼圖片
*
* @param content
* 二維碼的數據內容
* @param width
* 二維碼的寬(px)
* @param height
* 二維碼的高(px)
*
* @return 二維碼圖片信息BufferedImage
* @throws WriterException WriterException
* @date 2019/9/9 16:39
*/
private static BufferedImage genBufferedImage(String content, int width, int height)
throws WriterException {
Map<EncodeHintType, Object> hints = getHints();
// 生成一個矩陣(即:生成一個二維數組)
BitMatrix matrix;
matrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
int[] pixels = new int[width * height];
for (int y = 0; y < matrix.getHeight(); y++) {
for (int x = 0; x < matrix.getWidth(); x++) {
// 控制二維碼顏色, 前面那個爲 二維碼的顏色, 後面那個爲底色
pixels[y * width + x] = matrix.get(x, y) ? qrCodeColor : qrCodeBackgroundColor;
}
}
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
image.getRaster().setDataElements(0, 0, width, height, pixels);
return image;
}
/**
* 生成(帶內嵌圖片的)二維碼圖片信息BufferedImage
* <p>
* 注:BufferedImage即爲二維碼圖片的數據容器,可以利用其進一步生成二維碼圖片
*
* @param content
* 二維碼的數據內容
* @param width
* 二維碼的寬(px)
* @param height
* 二維碼的高(px)
* @param embeddedImagePath
* 二維碼中要嵌套的圖片path
*
* @return 二維碼圖片信息BufferedImage
* @throws IOException IOException
* @throws WriterException WriterException
* @date 2019/9/9 16:39
*/
private static BufferedImage genBufferedImageWithEmbeddedImg(String content, int width,
int height, String embeddedImagePath)
throws WriterException, IOException {
// 讀取要嵌套的圖像,並將其壓縮到指定的寬高
BufferedImage scaleImage = scale(embeddedImagePath, embeddedImgDefaultWidth,
embeddedImgDefaultHeight, false);
int embeddedImgFinalWidth = scaleImage.getWidth();
int embeddedImgFinalHalfWidth = embeddedImgFinalWidth / 2;
int embeddedImgFinalHeight = scaleImage.getHeight();
int embeddedImgFinalHalfHeight = embeddedImgFinalHeight / 2;
int[][] srcPixels = new int[embeddedImgFinalWidth][embeddedImgFinalHeight];
for (int i = 0; i < embeddedImgFinalWidth; i++) {
for (int j = 0; j < embeddedImgFinalHeight; j++) {
srcPixels[i][j] = scaleImage.getRGB(i, j);
}
}
Map<EncodeHintType, Object> hint = getHints();
// 生成一個矩陣(即:生成一個二維數組)( 注:下面會在這個矩陣上繪圖)
BitMatrix matrix = multiFormatWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hint);
// 二維碼矩陣轉爲一維像素數組
int qrCodeHalfWidth = matrix.getWidth() / 2;
int qrCodeHalfHeight = matrix.getHeight() / 2;
int[] pixels = new int[width * height];
// 是否處於【嵌入圖片 與 二維碼圖片 之間的框】裏
boolean atEmbeddedImgFrameArea;
// 是否處於【嵌入圖片的位置】裏
boolean atEmbeddedImgArea;
// 內嵌的圖片(左下角) 在 二維碼圖片 中的座標X
int embeddedImgCoordinatesX = qrCodeHalfWidth - embeddedImgFinalHalfWidth;
// 內嵌的圖片(左下角) 在 二維碼圖片 中的座標Y
int embeddedImgCoordinatesY = qrCodeHalfHeight - embeddedImgFinalHalfHeight;
for (int y = 0; y < matrix.getHeight(); y++) {
for (int x = 0; x < matrix.getWidth(); x++) {
atEmbeddedImgArea = x > qrCodeHalfWidth - embeddedImgFinalHalfWidth
&& x < qrCodeHalfWidth + embeddedImgFinalHalfWidth
&& y > qrCodeHalfHeight - embeddedImgFinalHalfHeight
&& y < qrCodeHalfHeight + embeddedImgFinalHalfHeight;
atEmbeddedImgFrameArea = (x > qrCodeHalfWidth - embeddedImgFinalHalfWidth - frameWidth
&& x < qrCodeHalfWidth - embeddedImgFinalHalfWidth + frameWidth
&& y > qrCodeHalfHeight - embeddedImgFinalHalfHeight - frameWidth
&& y < qrCodeHalfHeight + embeddedImgFinalHalfHeight + frameWidth)
||
(x > qrCodeHalfWidth + embeddedImgFinalHalfWidth - frameWidth
&& x < qrCodeHalfWidth + embeddedImgFinalHalfWidth + frameWidth
&& y > qrCodeHalfHeight - embeddedImgFinalHalfHeight - frameWidth
&& y < qrCodeHalfHeight + embeddedImgFinalHalfHeight + frameWidth)
||
(x > qrCodeHalfWidth - embeddedImgFinalHalfWidth - frameWidth
&& x < qrCodeHalfWidth + embeddedImgFinalHalfWidth + frameWidth
&& y > qrCodeHalfHeight - embeddedImgFinalHalfHeight - frameWidth
&& y < qrCodeHalfHeight - embeddedImgFinalHalfHeight + frameWidth)
||
(x > qrCodeHalfWidth - embeddedImgFinalHalfWidth - frameWidth
&& x < qrCodeHalfWidth + embeddedImgFinalHalfWidth + frameWidth
&& y > qrCodeHalfHeight + embeddedImgFinalHalfHeight - frameWidth
&& y < qrCodeHalfHeight + embeddedImgFinalHalfHeight + frameWidth);
// 在二維碼矩陣數像素組裏 嵌入 嵌入圖片的像素數組
if (atEmbeddedImgArea) {
pixels[y * width + x] = srcPixels[x - embeddedImgCoordinatesX][y - embeddedImgCoordinatesY];
}
// 嵌入圖片 與 二維碼圖片 之間的框(設置其顏色)
else if (atEmbeddedImgFrameArea) {
pixels[y * width + x] = frameWidthColor;
// 二維碼圖片區域
} else {
// 控制二維碼顏色, 前面那個爲 二維碼的顏色, 後面那個爲底色
pixels[y * width + x] = matrix.get(x, y) ? qrCodeColor : qrCodeBackgroundColor;
}
}
}
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
image.getRaster().setDataElements(0, 0, width, height, pixels);
return image;
}
/**
* 獲取hint信息
*
* @return hint
* @date 2019/9/10 1:14
*/
private static Map<EncodeHintType, Object> getHints() {
Map<EncodeHintType, Object> hint = new HashMap<>(8);
hint.put(EncodeHintType.CHARACTER_SET, "utf-8");
hint.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
// 二維碼整體白框
hint.put(EncodeHintType.MARGIN, 1);
return hint;
}
/**
* 把傳入的原始圖像按高度(或寬度)進行縮小,生成符合要求的圖標的BufferedImage信息
* <p>
* 注:若圖片原來的大小比給定的寬高小,那麼不會進行放大,而是仍然保持原來的大小。
*
* @param srcImageFile
* 源文件地址
* @param width
* (縮小後的)寬度
* @param height
* (縮小後的)高度
* @param autoFill
* 是否對按比例縮小後的圖片進行填充,使其完全達到給定的寬高。
* <p>
* 注:圖片按照最長邊與對應的寬(或高)的比例縮小後,最長邊縮小後的尺寸與給定的寬(或高)一致了,
* 但是其高(或寬),與縮小後的最短邊不一定一樣。此時,我們可以考慮使用給定的顏色對
* 其進行填充。如: 想要嵌入進去的圖片的實際寬是80, 高是100; 但是這裏指定縮小後的尺寸爲
* 寬爲10,高爲10。 由下面的邏輯可知:縮小比例爲0.1, 縮小後,圖片的寬就變成了8,高就變
* 成了10,此時高是滿足我們的要求的,但是寬差了2個像素。此時就可以將其進行填充了。
* <p>
* 注:建議爲false。
*
* @return 圖片的BufferedImage信息
* @throws IOException IOException
* @date 2019/9/10 1:59
*/
private static BufferedImage scale(String srcImageFile, int width, int height, boolean autoFill)
throws IOException {
// 縮小比例
double ratio;
File file = getFile(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 (autoFill) {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D graphic = image.createGraphics();
graphic.setColor(Color.red);
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;
}
/**
* 一行字符串拆分成多行
*/
private static String[] splitStrLines(String str, int len) {
int blocks = str.length() / len + 1;
String[] strArray = new String[blocks];
for (int i = 0; i < blocks; i++) {
if ((i + 1) * len > str.length()) {
strArray[i] = str.substring(i * len);
} else {
strArray[i] = str.substring(i * len, (i + 1) * len);
}
}
return strArray;
}
/**
* Writes a {@link BitMatrix} to {@link BufferedImage},
* file or stream. Provided here instead of core since it depends on
* Java SE libraries.
*/
private static class MatrixToImageWriter {
private static final MatrixToImageConfig DEFAULT_CONFIG = new MatrixToImageConfig();
private MatrixToImageWriter() {
}
/**
* Renders a {@link BitMatrix} as an image, where "false" bits are rendered
* as white, and "true" bits are rendered as black.
*/
private static BufferedImage toBufferedImage(BitMatrix matrix) {
return toBufferedImage(matrix, DEFAULT_CONFIG);
}
/**
* As {@link #toBufferedImage(BitMatrix)}, but allows customization of the output.
*/
private static BufferedImage toBufferedImage(BitMatrix matrix, MatrixToImageConfig config) {
int width = matrix.getWidth();
int height = matrix.getHeight();
BufferedImage image = new BufferedImage(width, height, config.getBufferedImageColorModel());
int onColor = config.getPixelOnColor();
int offColor = config.getPixelOffColor();
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
image.setRGB(x, y, matrix.get(x, y) ? onColor : offColor);
}
}
return image;
}
/**
* @deprecated use {@link #writeToPath(BitMatrix, String, Path)}
*/
@Deprecated
private static void writeToFile(BitMatrix matrix, String format, File file) throws IOException {
writeToPath(matrix, format, file.toPath());
}
/**
* Writes a {@link BitMatrix} to a file.
*
* @see #toBufferedImage(BitMatrix)
*/
private static void writeToPath(BitMatrix matrix, String format, Path file) throws IOException {
writeToPath(matrix, format, file, DEFAULT_CONFIG);
}
/**
* @deprecated use {@link #writeToPath(BitMatrix, String, Path, MatrixToImageConfig)}
*/
@Deprecated
private static void writeToFile(BitMatrix matrix, String format, File file,
MatrixToImageConfig config) throws IOException {
writeToPath(matrix, format, file.toPath(), config);
}
/**
* As {@link #writeToFile(BitMatrix, String, File)}, but allows customization of the output.
*/
@SuppressWarnings("all")
private static void writeToPath(BitMatrix matrix, String format, Path file,
MatrixToImageConfig config) throws IOException {
BufferedImage image = toBufferedImage(matrix, config);
if (!ImageIO.write(image, format, file.toFile())) {
throw new IOException("Could not write an image of format " + format + " to " + file);
}
}
/**
* Writes a {@link BitMatrix} to a stream.
*
* @see #toBufferedImage(BitMatrix)
*/
private static void writeToStream(BitMatrix matrix, String format, OutputStream stream)
throws IOException {
writeToStream(matrix, format, stream, DEFAULT_CONFIG);
}
/**
* As {@link #writeToStream(BitMatrix, String, OutputStream)}, but allows customization
* of the output.
*/
private static void writeToStream(BitMatrix matrix, String format, OutputStream stream,
MatrixToImageConfig config) throws IOException {
BufferedImage image = toBufferedImage(matrix, config);
if (!ImageIO.write(image, format, stream)) {
throw new IOException("Could not write an image of format 【" + format + "】");
}
}
/**
* 配置類封裝
* <p>
* Encapsulates custom configuration used in methods of {@link MatrixToImageWriter}.
*/
private static class MatrixToImageConfig {
private static final int BLACK = Color.BLACK.getRGB();
private static final int WHITE = Color.WHITE.getRGB();
private final int onColor;
private final int offColor;
/**
* Creates a default config with on color {@link #BLACK} and off color {@link #WHITE},
* generating normal
* black-on-white barcodes.
*/
private MatrixToImageConfig() {
this(BLACK, WHITE);
}
/**
* @param onColor
* pixel on color, specified as an ARGB value as an int
* @param offColor
* pixel off color, specified as an ARGB value as an int
*/
private MatrixToImageConfig(int onColor, int offColor) {
this.onColor = onColor;
this.offColor = offColor;
}
private int getPixelOnColor() {
return onColor;
}
private int getPixelOffColor() {
return offColor;
}
private int getBufferedImageColorModel() {
// Use faster BINARY if colors match default
return onColor == BLACK && offColor == WHITE
? BufferedImage.TYPE_BYTE_BINARY : BufferedImage.TYPE_INT_RGB;
}
}
}
}
聲明: 此工具類由本人二次整理改造後分享給大家,原編寫者未知,雖然本人重寫了大部分邏輯代碼,但是核心部分仍然是採用的原來的代碼。若涉及侵權問題,請隨時聯繫筆者。
^_^ 如有不當之處,歡迎指正
^_^ 參考鏈接
https://blog.csdn.net/jam_fanatic/article/details/82818857
^_^ 本文已經被收錄進《程序員成長筆記(六)》,筆者JustryDeng