使用 zxing 生成具有自定义点形状的 QR 码
Generate QR codes with custom dot shapes using zxing
我正在编写一个应用程序来生成具有自定义点形状的 QR 码。使用 zxing 执行此操作的最佳方法是什么?
到目前为止,我已经翻遍了源代码,我看到数据位是写在 com.google.zxing.qrcode.encoder.MatrixUtil.embedDataBits() 中的。我想我可以在此函数的末尾添加一些代码,这样我就可以屏蔽这些点,但我不确定如何在 Java 中执行此操作。我不能扩展 class 因为它被声明为最终的。这是个好主意吗?如果是的话,我将如何以这种方式扩展此方法?
我一直在寻找的另一个选项涉及 post-处理 QRCode 生成的图像,但我认为这真的很复杂,因为我必须找到一种方法来辨别定位中的点正方形。
有没有更好的方法来完成我想做的事情?除了 zxing 之外,是否还有另一个 QR 码库可以开箱即用地完成我想要做的事情?
P.S。我要注意这不是 this question 的重复,尽管关键字相似。
我最终分叉了 zxing 并使用 JitPack to include it with Maven. I implemented functionality for RGB masking and for drawing circles and outlined squares as dot shapes. Here is the repository: https://github.com/flotwig/zxing
以下 java 代码使用 zxing 制作带有圆点和圆形取景器图案(自定义呈现样式)的 QR 码图像。这可以适应其他自定义渲染样式。
我直接使用编码器 class 并绕过 QRCodeWriter 和 MatrixToImageWriter 以获得足够的控制权来改变渲染。为了改变取景器图案,我使用了取景器图案始终为 7 个点 wide/tall 的事实。否则我将不得不创建一个自定义版本的 MatrixUtil(也许还有编码器)。
生成的二维码图像示例:
public static void main(String[] args) {
try {
generateQRCodeImage("https://www.google.com", 300, 300, "./MyQRCode.png");
} catch (Exception e) {
e.printStackTrace();
}
}
private static void generateQRCodeImage(String text, int width, int height, String filePath) throws WriterException, IOException {
final Map<EncodeHintType, Object> encodingHints = new HashMap<>();
encodingHints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
QRCode code = Encoder.encode(text, ErrorCorrectionLevel.H, encodingHints);
BufferedImage image = renderQRImage(code, width, height, 4);
try (FileOutputStream stream = new FileOutputStream(filePath)) {
stream.write(bufferedImageToBytes(image));
}
}
private static BufferedImage renderQRImage(QRCode code, int width, int height, int quietZone) {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = image.createGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.setBackground(Color.white);
graphics.clearRect(0, 0, width, height);
graphics.setColor(Color.black);
ByteMatrix input = code.getMatrix();
if (input == null) {
throw new IllegalStateException();
}
int inputWidth = input.getWidth();
int inputHeight = input.getHeight();
int qrWidth = inputWidth + (quietZone * 2);
int qrHeight = inputHeight + (quietZone * 2);
int outputWidth = Math.max(width, qrWidth);
int outputHeight = Math.max(height, qrHeight);
int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);
int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
int topPadding = (outputHeight - (inputHeight * multiple)) / 2;
final int FINDER_PATTERN_SIZE = 7;
final float CIRCLE_SCALE_DOWN_FACTOR = 21f/30f;
int circleSize = (int) (multiple * CIRCLE_SCALE_DOWN_FACTOR);
for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
if (input.get(inputX, inputY) == 1) {
if (!(inputX <= FINDER_PATTERN_SIZE && inputY <= FINDER_PATTERN_SIZE ||
inputX >= inputWidth - FINDER_PATTERN_SIZE && inputY <= FINDER_PATTERN_SIZE ||
inputX <= FINDER_PATTERN_SIZE && inputY >= inputHeight - FINDER_PATTERN_SIZE)) {
graphics.fillOval(outputX, outputY, circleSize, circleSize);
}
}
}
}
int circleDiameter = multiple * FINDER_PATTERN_SIZE;
drawFinderPatternCircleStyle(graphics, leftPadding, topPadding, circleDiameter);
drawFinderPatternCircleStyle(graphics, leftPadding + (inputWidth - FINDER_PATTERN_SIZE) * multiple, topPadding, circleDiameter);
drawFinderPatternCircleStyle(graphics, leftPadding, topPadding + (inputHeight - FINDER_PATTERN_SIZE) * multiple, circleDiameter);
return image;
}
private static void drawFinderPatternCircleStyle(Graphics2D graphics, int x, int y, int circleDiameter) {
final int WHITE_CIRCLE_DIAMETER = circleDiameter*5/7;
final int WHITE_CIRCLE_OFFSET = circleDiameter/7;
final int MIDDLE_DOT_DIAMETER = circleDiameter*3/7;
final int MIDDLE_DOT_OFFSET = circleDiameter*2/7;
graphics.setColor(Color.black);
graphics.fillOval(x, y, circleDiameter, circleDiameter);
graphics.setColor(Color.white);
graphics.fillOval(x + WHITE_CIRCLE_OFFSET, y + WHITE_CIRCLE_OFFSET, WHITE_CIRCLE_DIAMETER, WHITE_CIRCLE_DIAMETER);
graphics.setColor(Color.black);
graphics.fillOval(x + MIDDLE_DOT_OFFSET, y + MIDDLE_DOT_OFFSET, MIDDLE_DOT_DIAMETER, MIDDLE_DOT_DIAMETER);
}
Maven 依赖项:
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.4.0</version>
</dependency>
我正在编写一个应用程序来生成具有自定义点形状的 QR 码。使用 zxing 执行此操作的最佳方法是什么?
到目前为止,我已经翻遍了源代码,我看到数据位是写在 com.google.zxing.qrcode.encoder.MatrixUtil.embedDataBits() 中的。我想我可以在此函数的末尾添加一些代码,这样我就可以屏蔽这些点,但我不确定如何在 Java 中执行此操作。我不能扩展 class 因为它被声明为最终的。这是个好主意吗?如果是的话,我将如何以这种方式扩展此方法?
我一直在寻找的另一个选项涉及 post-处理 QRCode 生成的图像,但我认为这真的很复杂,因为我必须找到一种方法来辨别定位中的点正方形。
有没有更好的方法来完成我想做的事情?除了 zxing 之外,是否还有另一个 QR 码库可以开箱即用地完成我想要做的事情?
P.S。我要注意这不是 this question 的重复,尽管关键字相似。
我最终分叉了 zxing 并使用 JitPack to include it with Maven. I implemented functionality for RGB masking and for drawing circles and outlined squares as dot shapes. Here is the repository: https://github.com/flotwig/zxing
以下 java 代码使用 zxing 制作带有圆点和圆形取景器图案(自定义呈现样式)的 QR 码图像。这可以适应其他自定义渲染样式。
我直接使用编码器 class 并绕过 QRCodeWriter 和 MatrixToImageWriter 以获得足够的控制权来改变渲染。为了改变取景器图案,我使用了取景器图案始终为 7 个点 wide/tall 的事实。否则我将不得不创建一个自定义版本的 MatrixUtil(也许还有编码器)。
生成的二维码图像示例:
public static void main(String[] args) {
try {
generateQRCodeImage("https://www.google.com", 300, 300, "./MyQRCode.png");
} catch (Exception e) {
e.printStackTrace();
}
}
private static void generateQRCodeImage(String text, int width, int height, String filePath) throws WriterException, IOException {
final Map<EncodeHintType, Object> encodingHints = new HashMap<>();
encodingHints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
QRCode code = Encoder.encode(text, ErrorCorrectionLevel.H, encodingHints);
BufferedImage image = renderQRImage(code, width, height, 4);
try (FileOutputStream stream = new FileOutputStream(filePath)) {
stream.write(bufferedImageToBytes(image));
}
}
private static BufferedImage renderQRImage(QRCode code, int width, int height, int quietZone) {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D graphics = image.createGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.setBackground(Color.white);
graphics.clearRect(0, 0, width, height);
graphics.setColor(Color.black);
ByteMatrix input = code.getMatrix();
if (input == null) {
throw new IllegalStateException();
}
int inputWidth = input.getWidth();
int inputHeight = input.getHeight();
int qrWidth = inputWidth + (quietZone * 2);
int qrHeight = inputHeight + (quietZone * 2);
int outputWidth = Math.max(width, qrWidth);
int outputHeight = Math.max(height, qrHeight);
int multiple = Math.min(outputWidth / qrWidth, outputHeight / qrHeight);
int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
int topPadding = (outputHeight - (inputHeight * multiple)) / 2;
final int FINDER_PATTERN_SIZE = 7;
final float CIRCLE_SCALE_DOWN_FACTOR = 21f/30f;
int circleSize = (int) (multiple * CIRCLE_SCALE_DOWN_FACTOR);
for (int inputY = 0, outputY = topPadding; inputY < inputHeight; inputY++, outputY += multiple) {
for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
if (input.get(inputX, inputY) == 1) {
if (!(inputX <= FINDER_PATTERN_SIZE && inputY <= FINDER_PATTERN_SIZE ||
inputX >= inputWidth - FINDER_PATTERN_SIZE && inputY <= FINDER_PATTERN_SIZE ||
inputX <= FINDER_PATTERN_SIZE && inputY >= inputHeight - FINDER_PATTERN_SIZE)) {
graphics.fillOval(outputX, outputY, circleSize, circleSize);
}
}
}
}
int circleDiameter = multiple * FINDER_PATTERN_SIZE;
drawFinderPatternCircleStyle(graphics, leftPadding, topPadding, circleDiameter);
drawFinderPatternCircleStyle(graphics, leftPadding + (inputWidth - FINDER_PATTERN_SIZE) * multiple, topPadding, circleDiameter);
drawFinderPatternCircleStyle(graphics, leftPadding, topPadding + (inputHeight - FINDER_PATTERN_SIZE) * multiple, circleDiameter);
return image;
}
private static void drawFinderPatternCircleStyle(Graphics2D graphics, int x, int y, int circleDiameter) {
final int WHITE_CIRCLE_DIAMETER = circleDiameter*5/7;
final int WHITE_CIRCLE_OFFSET = circleDiameter/7;
final int MIDDLE_DOT_DIAMETER = circleDiameter*3/7;
final int MIDDLE_DOT_OFFSET = circleDiameter*2/7;
graphics.setColor(Color.black);
graphics.fillOval(x, y, circleDiameter, circleDiameter);
graphics.setColor(Color.white);
graphics.fillOval(x + WHITE_CIRCLE_OFFSET, y + WHITE_CIRCLE_OFFSET, WHITE_CIRCLE_DIAMETER, WHITE_CIRCLE_DIAMETER);
graphics.setColor(Color.black);
graphics.fillOval(x + MIDDLE_DOT_OFFSET, y + MIDDLE_DOT_OFFSET, MIDDLE_DOT_DIAMETER, MIDDLE_DOT_DIAMETER);
}
Maven 依赖项:
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.4.0</version>
</dependency>