cn.z.qrcode.encoder.QRCode Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of qrcode-encoder Show documentation
Show all versions of qrcode-encoder Show documentation
QR Code Encoder For Java 二维码生成器Java版
The newest version!
package cn.z.qrcode.encoder;
import java.nio.charset.StandardCharsets;
/**
* 二维码
*
*
* createDate 2023/05/29 11:11:11
*
*
* @author ALI[[email protected]]
* @since 1.0.0
**/
public class QRCode {
/**
* 纠错等级
*/
public final int Level;
/**
* 编码模式
*/
public final int Mode;
/**
* 版本
*/
public final Version Version;
/**
* 版本号
*/
public final int VersionNumber;
/**
* 掩模模板
*/
public final MaskPattern MaskPattern;
/**
* 掩模模板号
*/
public final int MaskPatternNumber;
/**
* 矩阵
* false白 true黑
*/
public final boolean[][] Matrix;
/**
* 构造二维码
* 纠错等级 0 L 7%
* 编码模式 自动探测
* 版本号 最小版本
*
* @param content 内容
*/
public QRCode(String content) {
this(content, null, null, null);
}
/**
* 构造二维码
* 编码模式 自动探测
* 版本号 最小版本
*
* @param content 内容
* @param level 纠错等级
* 0 L 7%
* 1 M 15%
* 2 Q 25%
* 3 H 30%
*/
public QRCode(String content, Integer level) {
this(content, level, null, null);
}
/**
* 构造二维码
* 版本号 最小版本
*
* @param content 内容
* @param level 纠错等级
* 0 L 7%
* 1 M 15%
* 2 Q 25%
* 3 H 30%
* @param mode 编码模式
* 0 NUMERIC 数字0-9
* 1 ALPHANUMERIC 数字0-9、大写字母A-Z、符号(空格)$%*+-./:
* 2 BYTE(ISO-8859-1)
* 3 BYTE(UTF-8)
*/
public QRCode(String content, Integer level, Integer mode) {
this(content, level, mode, null);
}
/**
* 构造二维码
*
* @param content 内容
* @param level 纠错等级
* 0 L 7%
* 1 M 15%
* 2 Q 25%
* 3 H 30%
* @param mode 编码模式
* 0 NUMERIC 数字0-9
* 1 ALPHANUMERIC 数字0-9、大写字母A-Z、符号(空格)$%*+-./:
* 2 BYTE(ISO-8859-1)
* 3 BYTE(UTF-8)
* @param versionNumber 版本号(默认最小版本)
* [1,40]
*/
public QRCode(String content, Integer level, Integer mode, Integer versionNumber) {
int levelValue;
int modeValue;
/* 数据 */
// 纠错等级
if (level == null) {
levelValue = 0;
} else if (level < 0 || level > 3) {
throw new QRCodeException("纠错等级 " + level + " 不合法!应为 [0,3]");
} else {
levelValue = level;
}
Level = levelValue;
// 编码模式
if (mode == null) {
modeValue = DetectionMode(content);
} else if (mode < 0 || mode > 3) {
throw new QRCodeException("编码模式 " + mode + " 不合法!应为 [0,3]");
} else {
int detectionMode = DetectionMode(content);
if (mode < detectionMode) {
throw new QRCodeException("编码模式 " + mode + " 太小!最小为 " + detectionMode);
}
modeValue = mode;
}
Mode = modeValue;
// 内容bytes
byte[] bytesRaw = content.getBytes(StandardCharsets.UTF_8);
int[] contentBytes = new int[bytesRaw.length];
for (int i = 0; i < contentBytes.length; i++) {
contentBytes[i] = (bytesRaw[i] & 0xFF);
}
// 版本
Version = new Version(contentBytes.length, Level, Mode, versionNumber);
VersionNumber = Version.VersionNumber;
// 数据bits
boolean[] dataBits = new boolean[Version.DataBits];
// 填充数据
switch (Mode) {
// 填充编码模式为NUMERIC的数据
case 0: {
ModeNumbers(dataBits, contentBytes, Version);
break;
}
// 填充编码模式为ALPHANUMERIC的数据
case 1: {
ModeAlphaNumeric(dataBits, contentBytes, Version);
break;
}
// 填充编码模式为BYTE编码格式为ISO-8859-1的数据
case 2: {
ModeByteIso88591(dataBits, contentBytes, Version);
break;
}
// 填充编码模式为BYTE编码格式为UTF-8的数据
default: {
ModeByteUtf8(dataBits, contentBytes, Version);
break;
}
}
/* 纠错 */
int[][] ec = Version.Ec;
// 数据块数 或 纠错块数
int blocks = 0;
for (int i = 0; i < Version.Ec.length; i++) {
blocks += Version.Ec[i][0];
}
// 纠错块字节数
int ecBlockBytes = (Version.DataAndEcBits - Version.DataBits) / 8 / blocks;
int[][] dataBlocks = new int[blocks][];
int[][] ecBlocks = new int[blocks][];
int blockNum = 0;
int dataByteNum = 0;
for (int[] e : ec) {
int count = e[0];
int dataBytes = e[1];
for (int j = 0; j < count; j++) {
// 数据块
int[] dataBlock = QRCodeUtils.GetBytes(dataBits, dataByteNum * 8, dataBytes);
dataBlocks[blockNum] = dataBlock;
// 纠错块
ecBlocks[blockNum] = ReedSolomon.Encoder(dataBlock, ecBlockBytes);
blockNum++;
dataByteNum += dataBytes;
}
}
/* 交叉数据和纠错 */
boolean[] dataAndEcBits = new boolean[Version.DataAndEcBits];
int dataBlockMaxBytes = dataBlocks[blocks - 1].length;
int dataAndEcBitPtr = 0;
for (int i = 0; i < dataBlockMaxBytes; i++) {
for (int j = 0; j < blocks; j++) {
if (dataBlocks[j].length > i) {
QRCodeUtils.AddBits(dataAndEcBits, dataAndEcBitPtr, dataBlocks[j][i], 8);
dataAndEcBitPtr += 8;
}
}
}
for (int i = 0; i < ecBlockBytes; i++) {
for (int j = 0; j < blocks; j++) {
QRCodeUtils.AddBits(dataAndEcBits, dataAndEcBitPtr, ecBlocks[j][i], 8);
dataAndEcBitPtr += 8;
}
}
/* 构造掩模模板 */
MaskPattern = new MaskPattern(dataAndEcBits, Version, Level);
MaskPatternNumber = MaskPattern.Best;
Matrix = QRCodeUtils.Convert(MaskPattern.Patterns[MaskPatternNumber], Version.Dimension);
}
/**
* 填充编码模式为NUMERIC的数据
*
* @param dataBits 数据bits
* @param contentBytes 内容bytes
* @param version 版本
*/
private static void ModeNumbers(boolean[] dataBits, int[] contentBytes, Version version) {
// 数据指针
int ptr = 0;
// 模式指示符(4bit) NUMERIC 0b0001=1
// 数据来源 ISO/IEC 18004-2015 -> 7.4.1 -> Table 2 -> QR Code symbols列Numbers行
QRCodeUtils.AddBits(dataBits, ptr, 1, 4);
ptr += 4;
// 内容字节数
int contentLength = contentBytes.length;
// `内容字节数`bit数(10/12/14bit)
int contentBytesBits = version.ContentBytesBits;
QRCodeUtils.AddBits(dataBits, ptr, contentLength, contentBytesBits);
ptr += contentBytesBits;
// 内容 3个字符10bit 2个字符7bit 1个字符4bit
for (int i = 0; i < contentLength - 2; i += 3) {
QRCodeUtils.AddBits(dataBits, ptr,
(contentBytes[i] - 48) * 100 + (contentBytes[i + 1] - 48) * 10 + contentBytes[i + 2] - 48, 10);
ptr += 10;
}
switch (contentLength % 3) {
case 2: {
QRCodeUtils.AddBits(dataBits, ptr,
(contentBytes[contentLength - 2] - 48) * 10 + contentBytes[contentLength - 1] - 48, 7);
ptr += 7;
break;
}
case 1: {
QRCodeUtils.AddBits(dataBits, ptr, contentBytes[contentLength - 1] - 48, 4);
ptr += 4;
break;
}
default:
}
// 结束符和补齐符
TerminatorAndPadding(dataBits, version.DataBits, ptr);
}
/**
* 填充编码模式为ALPHANUMERIC的数据
*
* @param dataBits 数据bits
* @param contentBytes 内容bytes
* @param version 版本
*/
private static void ModeAlphaNumeric(boolean[] dataBits, int[] contentBytes, Version version) {
// 数据指针
int ptr = 0;
// 模式指示符(4bit) ALPHANUMERIC 0b0010=2
// 数据来源 ISO/IEC 18004-2015 -> 7.4.1 -> Table 2 -> QR Code symbols列Alphanumeric行
QRCodeUtils.AddBits(dataBits, ptr, 2, 4);
ptr += 4;
// 内容字节数
int contentLength = contentBytes.length;
// `内容字节数`bit数(9/11/13bit)
int contentBytesBits = version.ContentBytesBits;
QRCodeUtils.AddBits(dataBits, ptr, contentLength, contentBytesBits);
ptr += contentBytesBits;
// 内容 2个字符11bit 1个字符6bit
for (int i = 0; i < contentLength - 1; i += 2) {
QRCodeUtils.AddBits(dataBits, ptr,
ALPHA_NUMERIC_TABLE[contentBytes[i]] * 45 + ALPHA_NUMERIC_TABLE[contentBytes[i + 1]], 11);
ptr += 11;
}
if (contentLength % 2 == 1) {
QRCodeUtils.AddBits(dataBits, ptr, ALPHA_NUMERIC_TABLE[contentBytes[contentLength - 1]], 6);
ptr += 6;
}
// 结束符和补齐符
TerminatorAndPadding(dataBits, version.DataBits, ptr);
}
/**
* 填充编码模式为BYTE编码格式为ISO-8859-1的数据
*
* @param dataBits 数据bits
* @param contentBytes 内容bytes
* @param version 版本
*/
private static void ModeByteIso88591(boolean[] dataBits, int[] contentBytes, Version version) {
// 数据指针
int ptr = 0;
// 模式指示符(4bit) BYTE 0b0100=4
// 数据来源 ISO/IEC 18004-2015 -> 7.4.1 -> Table 2 -> QR Code symbols列Byte行
QRCodeUtils.AddBits(dataBits, ptr, 4, 4);
ptr += 4;
// 内容字节数
int contentLength = contentBytes.length;
// `内容字节数`bit数(8/16bit)
int contentBytesBits = version.ContentBytesBits;
QRCodeUtils.AddBits(dataBits, ptr, contentLength, contentBytesBits);
ptr += contentBytesBits;
// 内容
for (int contentByte : contentBytes) {
QRCodeUtils.AddBits(dataBits, ptr, contentByte, 8);
ptr += 8;
}
// 结束符和补齐符
TerminatorAndPadding(dataBits, version.DataBits, ptr);
}
/**
* 填充编码模式为BYTE编码格式为UTF-8的数据
*
* @param dataBits 数据bits
* @param contentBytes 内容bytes
* @param version 版本
*/
private static void ModeByteUtf8(boolean[] dataBits, int[] contentBytes, Version version) {
// 数据指针
int ptr = 0;
// ECI模式指示符(4bit) 0b0111=7
// 数据来源 ISO/IEC 18004-2015 -> 7.4.1 -> Table 2 -> QR Code symbols列ECI行
QRCodeUtils.AddBits(dataBits, ptr, 7, 4);
ptr += 4;
// ECI指定符 UTF-8(8bit) 0b00011010=26
// 数据来源 ?
QRCodeUtils.AddBits(dataBits, ptr, 26, 8);
ptr += 8;
// 模式指示符(4bit) BYTE 0b0100=4
// 数据来源 ISO/IEC 18004-2015 -> 7.4.1 -> Table 2 -> QR Code symbols列Byte行
QRCodeUtils.AddBits(dataBits, ptr, 4, 4);
ptr += 4;
// 内容字节数
int contentLength = contentBytes.length;
// `内容字节数`bit数(8/16bit)
int contentBytesBits = version.ContentBytesBits;
QRCodeUtils.AddBits(dataBits, ptr, contentLength, contentBytesBits);
ptr += contentBytesBits;
// 内容
for (int contentByte : contentBytes) {
QRCodeUtils.AddBits(dataBits, ptr, contentByte, 8);
ptr += 8;
}
// 结束符和补齐符
TerminatorAndPadding(dataBits, version.DataBits, ptr);
}
/**
* 结束符和补齐符
*
* @param data 数据bits
* @param dataBits 数据bits数
* @param ptr 数据指针
*/
private static void TerminatorAndPadding(boolean[] data, int dataBits, int ptr) {
// 如果有刚好填满,则不需要结束符和补齐符
// 如果还剩1-8bit,需要1-8bit结束符,不用补齐符
// 如果还剩8+bit,先填充4bit结束符,再填充结束符使8bit对齐,再交替补齐符至填满
if (dataBits - ptr > 7) {
// 结束符(4bit)
// 数据来源 ISO/IEC 18004-2015 -> 7.4.9
ptr += 4;
// 结束符(8bit对齐)
ptr = (((ptr - 1) / 8) + 1) * 8;
// 补齐符 交替0b11101100=0xEC和0b00010001=0x11至填满
// 数据来源 ISO/IEC 18004-2015 -> 7.4.10
int count = (dataBits - ptr) / 8;
for (int i = 0; i < count; i++) {
if (i % 2 == 0) {
System.arraycopy(NUMBER_0xEC_8BITS, 0, data, ptr, 8);
} else {
System.arraycopy(NUMBER_0x11_8BITS, 0, data, ptr, 8);
}
ptr += 8;
}
}
}
/**
* 探测编码模式
*
* @param content 内容
* @return 编码模式
* 0 NUMERIC 数字0-9
* 1 ALPHANUMERIC 数字0-9、大写字母A-Z、符号(空格)$%*+-./:
* 2 BYTE(ISO-8859-1)
* 3 BYTE(UTF-8)
*/
private static int DetectionMode(String content) {
int length = content.length();
// 为了与ZXing结果保持一致,长度为0时使用BYTE(ISO-8859-1)编码
if (length == 0) {
return 2;
}
// BYTE(UTF-8)
for (int i = 0; i < length; i++) {
if (content.charAt(i) > 255) {
return 3;
}
}
// BYTE(ISO-8859-1)
for (int i = 0; i < length; i++) {
if (content.charAt(i) > 127 || ALPHA_NUMERIC_TABLE[content.charAt(i)] > 44) {
return 2;
}
}
// ALPHANUMERIC 数字0-9、大写字母A-Z、符号(空格)$%*+-./:
for (int i = 0; i < length; i++) {
if (ALPHA_NUMERIC_TABLE[content.charAt(i)] > 9) {
return 1;
}
}
// NUMERIC 数字0-9
return 0;
}
/**
* 数字0xEC
* 数据来源 ISO/IEC 18004-2015 -> 7.4.10 -> 11101100
*/
private static final boolean[] NUMBER_0xEC_8BITS = QRCodeUtils.GetBits(0xEC, 8);
/**
* 数字0x11
* 数据来源 ISO/IEC 18004-2015 -> 7.4.10 -> 00010001
*/
private static final boolean[] NUMBER_0x11_8BITS = QRCodeUtils.GetBits(0x11, 8);
/**
* ALPHANUMERIC模式映射表
* 数字0-9 [0x30,0x39] [0,9]
* 大写字母A-Z [0x41,0x5A] [10,35]
* (空格) [0x20] [36]
* $ [0x24] [37]
* % [0x25] [38]
* * [0x2A] [39]
* + [0x2B] [40]
* - [0x2D] [41]
* . [0x2E] [42]
* / [0x2F] [43]
* : [0x3A] [44]
* 数据来源 ISO/IEC 18004-2015 -> 7.4.5 -> Table 6
*/
private static final byte[] ALPHA_NUMERIC_TABLE = new byte[]{ //
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, // 0x00-0x0F
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, // 0x10-0x1F
36, 127, 127, 127, 37, 38, 127, 127, 127, 127, 39, 40, 127, 41, 42, 43, // 0x20-0x2F
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, 127, 127, 127, 127, 127, // 0x30-0x3F
127, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0x4F
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 127, 127, 127, 127, 127, // 0x50-0x5F
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, // 0x60-0x6F
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, // 0x70-0x7F
};
}