mirror of
https://gitee.com/dromara/hutool.git
synced 2025-04-05 17:37:59 +08:00
add BitMatrixRender
This commit is contained in:
parent
dfacb2c024
commit
89dd871af7
@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.swing.qrcode;
|
||||
|
||||
import org.dromara.hutool.core.lang.ansi.AnsiElement;
|
||||
import org.dromara.hutool.core.lang.ansi.AnsiEncoder;
|
||||
import org.dromara.hutool.swing.img.color.ColorUtil;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
/**
|
||||
* 二维码的AsciiArt表示
|
||||
*
|
||||
* @author Tom Xin
|
||||
*/
|
||||
public class QrAsciiArt {
|
||||
|
||||
private final BitMatrix matrix;
|
||||
private final QrConfig qrConfig;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
* @param matrix {@link BitMatrix}
|
||||
* @param qrConfig {@link QrConfig}
|
||||
*/
|
||||
public QrAsciiArt(final BitMatrix matrix, final QrConfig qrConfig) {
|
||||
this.matrix = matrix;
|
||||
this.qrConfig = qrConfig;
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnnecessaryUnicodeEscape")
|
||||
@Override
|
||||
public String toString() {
|
||||
final int width = matrix.getWidth();
|
||||
final int height = matrix.getHeight();
|
||||
|
||||
|
||||
final AnsiElement foreground = qrConfig.foreColor == null ? null : ColorUtil.toAnsiColor(qrConfig.foreColor, true, false);
|
||||
final AnsiElement background = qrConfig.backColor == null ? null : ColorUtil.toAnsiColor(qrConfig.backColor, true, true);
|
||||
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i <= height; i += 2) {
|
||||
final StringBuilder rowBuilder = new StringBuilder();
|
||||
for (int j = 0; j < width; j++) {
|
||||
final boolean tp = matrix.get(i, j);
|
||||
final boolean bt = i + 1 >= height || matrix.get(i + 1, j);
|
||||
if (tp && bt) {
|
||||
rowBuilder.append(' ');//'\u0020'
|
||||
} else if (tp) {
|
||||
rowBuilder.append('▄');//'\u2584'
|
||||
} else if (bt) {
|
||||
rowBuilder.append('▀');//'\u2580'
|
||||
} else {
|
||||
rowBuilder.append('█');//'\u2588'
|
||||
}
|
||||
}
|
||||
builder.append(AnsiEncoder.encode(foreground, background, rowBuilder)).append('\n');
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
@ -22,19 +22,19 @@ import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.common.GlobalHistogramBinarizer;
|
||||
import com.google.zxing.common.HybridBinarizer;
|
||||
import org.dromara.hutool.core.codec.binary.Base64;
|
||||
import org.dromara.hutool.core.io.IoUtil;
|
||||
import org.dromara.hutool.core.io.file.FileNameUtil;
|
||||
import org.dromara.hutool.core.io.file.FileUtil;
|
||||
import org.dromara.hutool.core.net.url.UrlUtil;
|
||||
import org.dromara.hutool.core.util.ObjUtil;
|
||||
import org.dromara.hutool.swing.img.ImgUtil;
|
||||
import org.dromara.hutool.swing.qrcode.render.AsciiArtRender;
|
||||
import org.dromara.hutool.swing.qrcode.render.BitMatrixRender;
|
||||
import org.dromara.hutool.swing.qrcode.render.ImageRender;
|
||||
import org.dromara.hutool.swing.qrcode.render.SVGRender;
|
||||
|
||||
import java.awt.Image;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.*;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@ -140,23 +140,11 @@ public class QrCodeUtil {
|
||||
*/
|
||||
public static File generate(final String content, final QrConfig config, final File targetFile) {
|
||||
final String extName = FileNameUtil.extName(targetFile);
|
||||
switch (extName) {
|
||||
case QR_TYPE_SVG:
|
||||
FileUtil.writeUtf8String(generateAsSvg(content, config), targetFile);
|
||||
break;
|
||||
case QR_TYPE_TXT:
|
||||
FileUtil.writeUtf8String(generateAsAsciiArt(content, config), targetFile);
|
||||
break;
|
||||
default:
|
||||
BufferedImage image = null;
|
||||
try {
|
||||
image = generate(content, config);
|
||||
ImgUtil.write(image, targetFile);
|
||||
} finally {
|
||||
ImgUtil.flush(image);
|
||||
}
|
||||
try (final BufferedOutputStream outputStream = FileUtil.getOutputStream(targetFile)) {
|
||||
generate(content, config, extName, outputStream);
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return targetFile;
|
||||
}
|
||||
|
||||
@ -183,22 +171,18 @@ public class QrCodeUtil {
|
||||
* @since 4.1.2
|
||||
*/
|
||||
public static void generate(final String content, final QrConfig config, final String imageType, final OutputStream out) {
|
||||
final BitMatrixRender render;
|
||||
switch (imageType) {
|
||||
case QR_TYPE_SVG:
|
||||
IoUtil.writeUtf8(out, false, generateAsSvg(content, config));
|
||||
render = new SVGRender(config);
|
||||
break;
|
||||
case QR_TYPE_TXT:
|
||||
IoUtil.writeUtf8(out, false, generateAsAsciiArt(content, config));
|
||||
render = new AsciiArtRender(config);
|
||||
break;
|
||||
default:
|
||||
BufferedImage img = null;
|
||||
try {
|
||||
img = generate(content, config);
|
||||
ImgUtil.write(img, imageType, out);
|
||||
} finally {
|
||||
ImgUtil.flush(img);
|
||||
}
|
||||
render = new ImageRender(config, imageType);
|
||||
}
|
||||
render.render(encode(content, config), out);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -223,7 +207,7 @@ public class QrCodeUtil {
|
||||
* @since 4.1.14
|
||||
*/
|
||||
public static BufferedImage generate(final String content, final QrConfig config) {
|
||||
return new QrImage(content, ObjUtil.defaultIfNull(config, QrConfig::new));
|
||||
return new ImageRender(ObjUtil.defaultIfNull(config, QrConfig::new), null).render(encode(content, config));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------------- encode
|
||||
@ -356,7 +340,9 @@ public class QrCodeUtil {
|
||||
* @return SVG矢量图(字符串)
|
||||
*/
|
||||
public static String toSVG(final BitMatrix matrix, final QrConfig config) {
|
||||
return new QrSVG(matrix, config).toString();
|
||||
final StringBuilder result = new StringBuilder();
|
||||
new SVGRender(config).render(matrix, result);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -374,13 +360,15 @@ public class QrCodeUtil {
|
||||
/**
|
||||
* BitMatrix转ASCII Art字符画形式的二维码
|
||||
*
|
||||
* @param bitMatrix BitMatrix
|
||||
* @param qrConfig QR设置
|
||||
* @param matrix BitMatrix
|
||||
* @param config QR设置
|
||||
* @return ASCII Art字符画形式的二维码
|
||||
* @since 5.8.6
|
||||
*/
|
||||
public static String toAsciiArt(final BitMatrix bitMatrix, final QrConfig qrConfig) {
|
||||
return new QrAsciiArt(bitMatrix, qrConfig).toString();
|
||||
public static String toAsciiArt(final BitMatrix matrix, final QrConfig config) {
|
||||
final StringBuilder result = new StringBuilder();
|
||||
new AsciiArtRender(config).render(matrix, result);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
// region ----- Private Methods
|
||||
|
@ -182,7 +182,7 @@ public class QrConfig {
|
||||
*
|
||||
* @return 前景色
|
||||
*/
|
||||
public int getForeColor() {
|
||||
public Integer getForeColor() {
|
||||
return foreColor;
|
||||
}
|
||||
|
||||
@ -207,7 +207,7 @@ public class QrConfig {
|
||||
*
|
||||
* @return 背景色
|
||||
*/
|
||||
public int getBackColor() {
|
||||
public Integer getBackColor() {
|
||||
return backColor;
|
||||
}
|
||||
|
||||
|
@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.swing.qrcode;
|
||||
|
||||
import org.dromara.hutool.swing.img.Img;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
import java.awt.Image;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
/**
|
||||
* 二维码图片封装
|
||||
*
|
||||
* @author Looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class QrImage extends BufferedImage {
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param content 文本内容
|
||||
* @param config {@link QrConfig} 二维码配置,包括宽度、高度、边距、颜色、格式等
|
||||
*/
|
||||
public QrImage(final String content, final QrConfig config) {
|
||||
this(QrCodeUtil.encode(content, config), config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param matrix {@link BitMatrix}
|
||||
* @param config {@link QrConfig},非空
|
||||
*/
|
||||
public QrImage(final BitMatrix matrix, final QrConfig config) {
|
||||
super(matrix.getWidth(), matrix.getHeight(), null == config.backColor ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
|
||||
init(matrix, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*
|
||||
* @param matrix {@link BitMatrix}
|
||||
* @param config {@link QrConfig}
|
||||
*/
|
||||
private void init(final BitMatrix matrix, final QrConfig config) {
|
||||
final int width = matrix.getWidth();
|
||||
final int height = matrix.getHeight();
|
||||
final Integer foreColor = config.foreColor;
|
||||
final Integer backColor = config.backColor;
|
||||
for (int x = 0; x < width; x++) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
if (matrix.get(x, y)) {
|
||||
setRGB(x, y, foreColor);
|
||||
} else if (null != backColor) {
|
||||
setRGB(x, y, backColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Image logoImg = config.img;
|
||||
if (null != logoImg && BarcodeFormat.QR_CODE == config.format) {
|
||||
// 只有二维码可以贴图
|
||||
final int qrWidth = getWidth();
|
||||
final int qrHeight = getHeight();
|
||||
final int imgWidth;
|
||||
final int imgHeight;
|
||||
// 按照最短的边做比例缩放
|
||||
if (qrWidth < qrHeight) {
|
||||
imgWidth = qrWidth / config.ratio;
|
||||
imgHeight = logoImg.getHeight(null) * imgWidth / logoImg.getWidth(null);
|
||||
} else {
|
||||
imgHeight = qrHeight / config.ratio;
|
||||
imgWidth = logoImg.getWidth(null) * imgHeight / logoImg.getHeight(null);
|
||||
}
|
||||
|
||||
// 原图片上直接绘制水印
|
||||
Img.from(this).pressImage(//
|
||||
Img.from(logoImg).round(config.imgRound).getImg(), // 圆角
|
||||
new Rectangle(imgWidth, imgHeight), // 位置
|
||||
1//不透明
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.swing.qrcode;
|
||||
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
import org.dromara.hutool.swing.img.ImgUtil;
|
||||
import org.dromara.hutool.swing.img.color.ColorUtil;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Image;
|
||||
|
||||
/**
|
||||
* 二维码的SVG表示
|
||||
*
|
||||
* @author Tom Xin
|
||||
*/
|
||||
public class QrSVG {
|
||||
|
||||
private final BitMatrix matrix;
|
||||
private final QrConfig qrConfig;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
* @param matrix {@link BitMatrix}
|
||||
* @param qrConfig {@link QrConfig}
|
||||
*/
|
||||
public QrSVG(final BitMatrix matrix, final QrConfig qrConfig) {
|
||||
this.matrix = matrix;
|
||||
this.qrConfig = qrConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final Image logoImg = qrConfig.img;
|
||||
final Integer foreColor = qrConfig.foreColor;
|
||||
final Integer backColor = qrConfig.backColor;
|
||||
final int ratio = qrConfig.ratio;
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
final int qrWidth = matrix.getWidth();
|
||||
int qrHeight = matrix.getHeight();
|
||||
final int moduleHeight = (qrHeight == 1) ? qrWidth / 2 : 1;
|
||||
for (int y = 0; y < qrHeight; y++) {
|
||||
for (int x = 0; x < qrWidth; x++) {
|
||||
if (matrix.get(x, y)) {
|
||||
sb.append(" M").append(x).append(",").append(y).append("h1v").append(moduleHeight).append("h-1z");
|
||||
}
|
||||
}
|
||||
}
|
||||
qrHeight *= moduleHeight;
|
||||
String logoBase64 = "";
|
||||
int logoWidth = 0;
|
||||
int logoHeight = 0;
|
||||
int logoX = 0;
|
||||
int logoY = 0;
|
||||
if (logoImg != null) {
|
||||
logoBase64 = ImgUtil.toBase64DataUri(logoImg, "png");
|
||||
// 按照最短的边做比例缩放
|
||||
if (qrWidth < qrHeight) {
|
||||
logoWidth = qrWidth / ratio;
|
||||
logoHeight = logoImg.getHeight(null) * logoWidth / logoImg.getWidth(null);
|
||||
} else {
|
||||
logoHeight = qrHeight / ratio;
|
||||
logoWidth = logoImg.getWidth(null) * logoHeight / logoImg.getHeight(null);
|
||||
}
|
||||
logoX = (qrWidth - logoWidth) / 2;
|
||||
logoY = (qrHeight - logoHeight) / 2;
|
||||
|
||||
}
|
||||
|
||||
final StringBuilder result = StrUtil.builder();
|
||||
result.append("<svg width=\"").append(qrWidth).append("\" height=\"").append(qrHeight).append("\" \n");
|
||||
if (backColor != null) {
|
||||
final Color back = new Color(backColor, true);
|
||||
result.append("style=\"background-color:").append(ColorUtil.toCssRgba(back)).append("\"\n");
|
||||
}
|
||||
result.append("viewBox=\"0 0 ").append(qrWidth).append(" ").append(qrHeight).append("\" \n");
|
||||
result.append("xmlns=\"http://www.w3.org/2000/svg\" \n");
|
||||
result.append("xmlns:xlink=\"http://www.w3.org/1999/xlink\" >\n");
|
||||
result.append("<path d=\"").append(sb).append("\" ");
|
||||
if (foreColor != null) {
|
||||
final Color fore = new Color(foreColor, true);
|
||||
result.append("stroke=\"").append(ColorUtil.toCssRgba(fore)).append("\"");
|
||||
}
|
||||
result.append(" /> \n");
|
||||
if (StrUtil.isNotBlank(logoBase64)) {
|
||||
result.append("<image xlink:href=\"").append(logoBase64).append("\" height=\"").append(logoHeight).append("\" width=\"").append(logoWidth).append("\" y=\"").append(logoY).append("\" x=\"").append(logoX).append("\" />\n");
|
||||
}
|
||||
result.append("</svg>");
|
||||
return result.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.swing.qrcode.render;
|
||||
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import org.dromara.hutool.core.io.IORuntimeException;
|
||||
import org.dromara.hutool.core.lang.ansi.AnsiElement;
|
||||
import org.dromara.hutool.core.lang.ansi.AnsiEncoder;
|
||||
import org.dromara.hutool.swing.img.color.ColorUtil;
|
||||
import org.dromara.hutool.swing.qrcode.QrConfig;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
/**
|
||||
* ASCII Art渲染
|
||||
*
|
||||
* @author Looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class AsciiArtRender implements BitMatrixRender {
|
||||
|
||||
private final QrConfig config;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param config 二维码配置
|
||||
*/
|
||||
public AsciiArtRender(final QrConfig config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(final BitMatrix matrix, final OutputStream out) {
|
||||
render(matrix, new OutputStreamWriter(out, config.getCharset()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染SVG
|
||||
*
|
||||
* @param matrix 二维码
|
||||
* @param writer 输出
|
||||
*/
|
||||
public void render(final BitMatrix matrix, final Appendable writer) {
|
||||
final int width = matrix.getWidth();
|
||||
final int height = matrix.getHeight();
|
||||
|
||||
final Integer foreColor = config.getForeColor();
|
||||
final AnsiElement foreground = foreColor == null ? null : ColorUtil.toAnsiColor(foreColor, true, false);
|
||||
final Integer backColor = config.getBackColor();
|
||||
final AnsiElement background = backColor == null ? null : ColorUtil.toAnsiColor(backColor, true, true);
|
||||
|
||||
try {
|
||||
for (int i = 0; i <= height; i += 2) {
|
||||
final StringBuilder rowBuilder = new StringBuilder();
|
||||
for (int j = 0; j < width; j++) {
|
||||
final boolean tp = matrix.get(i, j);
|
||||
final boolean bt = i + 1 >= height || matrix.get(i + 1, j);
|
||||
if (tp && bt) {
|
||||
rowBuilder.append(' ');//'\u0020'
|
||||
} else if (tp) {
|
||||
rowBuilder.append('▄');//'\u2584'
|
||||
} else if (bt) {
|
||||
rowBuilder.append('▀');//'\u2580'
|
||||
} else {
|
||||
rowBuilder.append('█');//'\u2588'
|
||||
}
|
||||
}
|
||||
writer.append(AnsiEncoder.encode(foreground, background, rowBuilder)).append('\n');
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.swing.qrcode.render;
|
||||
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* BitMatrix渲染接口
|
||||
*
|
||||
* @author Looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public interface BitMatrixRender {
|
||||
|
||||
/**
|
||||
* 渲染
|
||||
*
|
||||
* @param matrix 二维码矩阵
|
||||
* @param out 输出流
|
||||
*/
|
||||
void render(BitMatrix matrix, OutputStream out);
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.swing.qrcode.render;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import org.dromara.hutool.swing.img.Img;
|
||||
import org.dromara.hutool.swing.img.ImgUtil;
|
||||
import org.dromara.hutool.swing.qrcode.QrConfig;
|
||||
|
||||
import java.awt.Image;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* 二维码图片渲染器
|
||||
*
|
||||
* @author Looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class ImageRender implements BitMatrixRender {
|
||||
|
||||
private final QrConfig config;
|
||||
private final String imageType;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param config 二维码配置
|
||||
* @param imageType 图片类型
|
||||
*/
|
||||
public ImageRender(final QrConfig config, final String imageType) {
|
||||
this.config = config;
|
||||
this.imageType = imageType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(final BitMatrix matrix, final OutputStream out) {
|
||||
BufferedImage img = null;
|
||||
try {
|
||||
img = render(matrix);
|
||||
ImgUtil.write(img, imageType, out);
|
||||
} finally {
|
||||
ImgUtil.flush(img);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染
|
||||
*
|
||||
* @param matrix 二维码矩阵
|
||||
* @return 图片
|
||||
*/
|
||||
public BufferedImage render(final BitMatrix matrix) {
|
||||
final BufferedImage image = getBufferedImage(matrix);
|
||||
|
||||
final Image logo = config.getImg();
|
||||
if (null != logo && BarcodeFormat.QR_CODE == config.getFormat()) {
|
||||
pressLogo(image, logo);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图片
|
||||
*
|
||||
* @param matrix 二维码矩阵
|
||||
* @return 图片
|
||||
*/
|
||||
private BufferedImage getBufferedImage(final BitMatrix matrix) {
|
||||
final BufferedImage image = new BufferedImage(
|
||||
matrix.getWidth(),
|
||||
matrix.getHeight(),
|
||||
null == config.getBackColor() ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
final int width = matrix.getWidth();
|
||||
final int height = matrix.getHeight();
|
||||
final Integer foreColor = config.getForeColor();
|
||||
final Integer backColor = config.getBackColor();
|
||||
for (int x = 0; x < width; x++) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
if (matrix.get(x, y)) {
|
||||
image.setRGB(x, y, foreColor);
|
||||
} else if (null != backColor) {
|
||||
image.setRGB(x, y, backColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* 贴图
|
||||
*
|
||||
* @param image 二维码图片
|
||||
* @param logoImg logo图片
|
||||
*/
|
||||
private void pressLogo(final BufferedImage image, final Image logoImg) {
|
||||
// 只有二维码可以贴图
|
||||
final int qrWidth = image.getWidth();
|
||||
final int qrHeight = image.getHeight();
|
||||
final int imgWidth;
|
||||
final int imgHeight;
|
||||
// 按照最短的边做比例缩放
|
||||
if (qrWidth < qrHeight) {
|
||||
imgWidth = qrWidth / config.getRatio();
|
||||
imgHeight = logoImg.getHeight(null) * imgWidth / logoImg.getWidth(null);
|
||||
} else {
|
||||
imgHeight = qrHeight / config.getRatio();
|
||||
imgWidth = logoImg.getWidth(null) * imgHeight / logoImg.getHeight(null);
|
||||
}
|
||||
|
||||
// 原图片上直接绘制水印
|
||||
Img.from(image).pressImage(//
|
||||
Img.from(logoImg).round(config.getImgRound()).getImg(), // 圆角
|
||||
new Rectangle(imgWidth, imgHeight), // 位置
|
||||
1//不透明
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.swing.qrcode.render;
|
||||
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import org.dromara.hutool.core.io.IORuntimeException;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
import org.dromara.hutool.swing.img.ImgUtil;
|
||||
import org.dromara.hutool.swing.img.color.ColorUtil;
|
||||
import org.dromara.hutool.swing.qrcode.QrConfig;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Image;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
/**
|
||||
* SVG渲染器
|
||||
*
|
||||
* @author Looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class SVGRender implements BitMatrixRender {
|
||||
|
||||
private final QrConfig qrConfig;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param qrConfig 二维码配置
|
||||
*/
|
||||
public SVGRender(final QrConfig qrConfig) {
|
||||
this.qrConfig = qrConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(final BitMatrix matrix, final OutputStream out) {
|
||||
render(matrix, new OutputStreamWriter(out, qrConfig.getCharset()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染SVG
|
||||
*
|
||||
* @param matrix 二维码
|
||||
* @param writer 输出
|
||||
*/
|
||||
public void render(final BitMatrix matrix, final Appendable writer) {
|
||||
final Image logoImg = qrConfig.getImg();
|
||||
final Integer foreColor = qrConfig.getForeColor();
|
||||
final Integer backColor = qrConfig.getBackColor();
|
||||
final int ratio = qrConfig.getRatio();
|
||||
|
||||
final int qrWidth = matrix.getWidth();
|
||||
int qrHeight = matrix.getHeight();
|
||||
final int moduleHeight = (qrHeight == 1) ? qrWidth / 2 : 1;
|
||||
|
||||
qrHeight *= moduleHeight;
|
||||
String logoBase64 = "";
|
||||
int logoWidth = 0;
|
||||
int logoHeight = 0;
|
||||
int logoX = 0;
|
||||
int logoY = 0;
|
||||
if (logoImg != null) {
|
||||
logoBase64 = ImgUtil.toBase64DataUri(logoImg, "png");
|
||||
// 按照最短的边做比例缩放
|
||||
if (qrWidth < qrHeight) {
|
||||
logoWidth = qrWidth / ratio;
|
||||
logoHeight = logoImg.getHeight(null) * logoWidth / logoImg.getWidth(null);
|
||||
} else {
|
||||
logoHeight = qrHeight / ratio;
|
||||
logoWidth = logoImg.getWidth(null) * logoHeight / logoImg.getHeight(null);
|
||||
}
|
||||
logoX = (qrWidth - logoWidth) / 2;
|
||||
logoY = (qrHeight - logoHeight) / 2;
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
writer.append("<svg width=\"").append(String.valueOf(qrWidth)).append("\" height=\"").append(String.valueOf(qrHeight)).append("\" \n");
|
||||
if (backColor != null) {
|
||||
final Color back = new Color(backColor, true);
|
||||
writer.append("style=\"background-color:").append(ColorUtil.toCssRgba(back)).append("\"\n");
|
||||
}
|
||||
writer.append("viewBox=\"0 0 ").append(String.valueOf(qrWidth)).append(" ").append(String.valueOf(qrHeight)).append("\" \n");
|
||||
writer.append("xmlns=\"http://www.w3.org/2000/svg\" \n");
|
||||
writer.append("xmlns:xlink=\"http://www.w3.org/1999/xlink\" >\n");
|
||||
writer.append("<path d=\"");
|
||||
|
||||
// 数据
|
||||
for (int y = 0; y < qrHeight; y++) {
|
||||
for (int x = 0; x < qrWidth; x++) {
|
||||
if (matrix.get(x, y)) {
|
||||
writer.append(" M").append(String.valueOf(x)).append(",").append(String.valueOf(y)).append("h1v").append(String.valueOf(moduleHeight)).append("h-1z");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writer.append("\" ");
|
||||
if (foreColor != null) {
|
||||
final Color fore = new Color(foreColor, true);
|
||||
writer.append("stroke=\"").append(ColorUtil.toCssRgba(fore)).append("\"");
|
||||
}
|
||||
writer.append(" /> \n");
|
||||
if (StrUtil.isNotBlank(logoBase64)) {
|
||||
writer.append("<image xlink:href=\"").append(logoBase64)
|
||||
.append("\" height=\"").append(String.valueOf(logoHeight)).append("\" width=\"").append(String.valueOf(logoWidth))
|
||||
.append("\" y=\"").append(String.valueOf(logoY)).append("\" x=\"").append(String.valueOf(logoX)).append("\" />\n");
|
||||
}
|
||||
writer.append("</svg>");
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Hutool Team and hutool.cn
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 二维码渲染器
|
||||
*
|
||||
* @author Looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
package org.dromara.hutool.swing.qrcode.render;
|
Loading…
Reference in New Issue
Block a user