mirror of
https://gitee.com/dromara/hutool.git
synced 2025-04-05 17:37:59 +08:00
优化生成AsciiArt字符画二维码、SVG二维码
This commit is contained in:
parent
b5b1418c3e
commit
ea06be85e9
@ -3,20 +3,15 @@ package cn.hutool.extra.qrcode;
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.img.Img;
|
||||
import cn.hutool.core.img.ImgUtil;
|
||||
import cn.hutool.core.lang.ansi.*;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.lang.ansi.Ansi8BitColor;
|
||||
import cn.hutool.core.lang.ansi.AnsiElement;
|
||||
import cn.hutool.core.lang.ansi.AnsiEncoder;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.Binarizer;
|
||||
import com.google.zxing.BinaryBitmap;
|
||||
import com.google.zxing.DecodeHintType;
|
||||
import com.google.zxing.LuminanceSource;
|
||||
import com.google.zxing.MultiFormatReader;
|
||||
import com.google.zxing.MultiFormatWriter;
|
||||
import com.google.zxing.NotFoundException;
|
||||
import com.google.zxing.Result;
|
||||
import com.google.zxing.WriterException;
|
||||
import com.google.zxing.*;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.common.GlobalHistogramBinarizer;
|
||||
import com.google.zxing.common.HybridBinarizer;
|
||||
@ -27,6 +22,7 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -42,6 +38,9 @@ import java.util.Map;
|
||||
*/
|
||||
public class QrCodeUtil {
|
||||
|
||||
public static final String QR_TYPE_SVG = "svg";// SVG矢量图格式
|
||||
public static final String QR_TYPE_TXT = "txt";// Ascii Art字符画文本
|
||||
|
||||
/**
|
||||
* 生成代 logo 图片的 Base64 编码格式的二维码,以 String 形式表示
|
||||
*
|
||||
@ -186,8 +185,22 @@ public class QrCodeUtil {
|
||||
* @return 目标文件
|
||||
*/
|
||||
public static File generate(String content, int width, int height, File targetFile) {
|
||||
final BufferedImage image = generate(content, width, height);
|
||||
ImgUtil.write(image, targetFile);
|
||||
String extName = FileUtil.extName(targetFile);
|
||||
switch (extName) {
|
||||
case QR_TYPE_SVG:
|
||||
String svg = generateAsSvg(content, new QrConfig(width, height));
|
||||
FileUtil.writeString(svg, targetFile, StandardCharsets.UTF_8);
|
||||
break;
|
||||
case QR_TYPE_TXT:
|
||||
String txt = generateAsAsciiArt(content, new QrConfig(width, height));
|
||||
FileUtil.writeString(txt, targetFile, StandardCharsets.UTF_8);
|
||||
break;
|
||||
default:
|
||||
final BufferedImage image = generate(content, width, height);
|
||||
ImgUtil.write(image, targetFile);
|
||||
break;
|
||||
}
|
||||
|
||||
return targetFile;
|
||||
}
|
||||
|
||||
@ -201,23 +214,48 @@ public class QrCodeUtil {
|
||||
* @since 4.1.2
|
||||
*/
|
||||
public static File generate(String content, QrConfig config, File targetFile) {
|
||||
final BufferedImage image = generate(content, config);
|
||||
ImgUtil.write(image, targetFile);
|
||||
String extName = FileUtil.extName(targetFile);
|
||||
switch (extName) {
|
||||
case QR_TYPE_SVG:
|
||||
final String svg = generateAsSvg(content, config);
|
||||
FileUtil.writeString(svg, targetFile, StandardCharsets.UTF_8);
|
||||
break;
|
||||
case QR_TYPE_TXT:
|
||||
final String txt = generateAsAsciiArt(content, config);
|
||||
FileUtil.writeString(txt, targetFile, StandardCharsets.UTF_8);
|
||||
break;
|
||||
default:
|
||||
final BufferedImage image = generate(content, config);
|
||||
ImgUtil.write(image, targetFile);
|
||||
break;
|
||||
}
|
||||
return targetFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成二维码到输出流
|
||||
*
|
||||
* @param content 文本内容
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
* @param imageType 图片类型(图片扩展名),见{@link ImgUtil}
|
||||
* @param out 目标流
|
||||
* @param content 文本内容
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
* @param targetType 类型(图片扩展名),见{@link #QR_TYPE_SVG}、 {@link #QR_TYPE_TXT}、{@link ImgUtil}
|
||||
* @param out 目标流
|
||||
*/
|
||||
public static void generate(String content, int width, int height, String imageType, OutputStream out) {
|
||||
final BufferedImage image = generate(content, width, height);
|
||||
ImgUtil.write(image, imageType, out);
|
||||
public static void generate(String content, int width, int height, String targetType, OutputStream out) {
|
||||
switch (targetType) {
|
||||
case QR_TYPE_SVG:
|
||||
final String svg = generateAsSvg(content, new QrConfig(width, height));
|
||||
IoUtil.writeUtf8(out, false, svg);
|
||||
break;
|
||||
case QR_TYPE_TXT:
|
||||
final String txt = generateAsAsciiArt(content, new QrConfig(width, height));
|
||||
IoUtil.writeUtf8(out, false, txt);
|
||||
break;
|
||||
default:
|
||||
final BufferedImage image = generate(content, width, height);
|
||||
ImgUtil.write(image, targetType, out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -225,13 +263,25 @@ public class QrCodeUtil {
|
||||
*
|
||||
* @param content 文本内容
|
||||
* @param config 二维码配置,包括长、宽、边距、颜色等
|
||||
* @param imageType 图片类型(图片扩展名),见{@link ImgUtil}
|
||||
* @param targetType 类型(图片扩展名),见{@link #QR_TYPE_SVG}、 {@link #QR_TYPE_TXT}、{@link ImgUtil}
|
||||
* @param out 目标流
|
||||
* @since 4.1.2
|
||||
*/
|
||||
public static void generate(String content, QrConfig config, String imageType, OutputStream out) {
|
||||
final BufferedImage image = generate(content, config);
|
||||
ImgUtil.write(image, imageType, out);
|
||||
public static void generate(String content, QrConfig config, String targetType, OutputStream out) {
|
||||
switch (targetType) {
|
||||
case QR_TYPE_SVG:
|
||||
final String svg = generateAsSvg(content, config);
|
||||
IoUtil.writeUtf8(out, false, svg);
|
||||
break;
|
||||
case QR_TYPE_TXT:
|
||||
final String txt = generateAsAsciiArt(content, config);
|
||||
IoUtil.writeUtf8(out, false, txt);
|
||||
break;
|
||||
default:
|
||||
final BufferedImage image = generate(content, config);
|
||||
ImgUtil.write(image, targetType, out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -479,7 +529,7 @@ public class QrCodeUtil {
|
||||
* @since 5.8.6
|
||||
*/
|
||||
public static String toSVG(BitMatrix matrix, QrConfig qrConfig) {
|
||||
return toSVG(matrix, qrConfig.getForeColor(), qrConfig.getBackColor(), qrConfig.img, qrConfig.getRatio());
|
||||
return toSVG(matrix, qrConfig.foreColor, qrConfig.backColor, qrConfig.img, qrConfig.getRatio());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -524,16 +574,24 @@ public class QrCodeUtil {
|
||||
logoY = (qrHeight - logoHeight) / 2;
|
||||
|
||||
}
|
||||
|
||||
Color fore = new Color(foreColor, true);
|
||||
Color back = new Color(backColor, true);
|
||||
return "<svg width=\"" + qrWidth + "\" height=\"" + qrHeight + "\" \n" +
|
||||
(backColor == null ? "" : "style=\"background-color:rgba(" + back.getRed() + "," + back.getGreen() + "," + back.getBlue() + "," + back.getAlpha() + ")\"\n") +
|
||||
"viewBox=\"0 0 " + qrWidth + " " + qrHeight + "\" \n" +
|
||||
"xmlns=\"http://www.w3.org/2000/svg\" \n" +
|
||||
"xmlns:xlink=\"http://www.w3.org/1999/xlink\" >\n" +
|
||||
"<path d=\"" + sb + "\" stroke=\"rgba(" + fore.getRed() + "," + fore.getGreen() + "," + fore.getBlue() + "," + fore.getAlpha() + ")\" /> \n" +
|
||||
(StrUtil.isBlank(logoBase64) ? "" : "<image xlink:href=\"" + logoBase64 + "\" height=\"" + logoHeight + "\" width=\"" + logoWidth + "\" y=\"" + logoY + "\" x=\"" + logoX + "\" />\n") +
|
||||
"</svg>";
|
||||
|
||||
StringBuilder result = StrUtil.builder();
|
||||
result.append("<svg width=\"" + qrWidth + "\" height=\"" + qrHeight + "\" \n");
|
||||
if (backColor != null) {
|
||||
Color back = new Color(backColor, true);
|
||||
result.append("style=\"background-color:rgba(" + back.getRed() + "," + back.getGreen() + "," + back.getBlue() + "," + back.getAlpha() + ")\"\n");
|
||||
}
|
||||
result.append("viewBox=\"0 0 " + qrWidth + " " + qrHeight + "\" \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=\"" + sb + "\" stroke=\"rgba(" + fore.getRed() + "," + fore.getGreen() + "," + fore.getBlue() + "," + fore.getAlpha() + ")\" /> \n");
|
||||
if (StrUtil.isNotBlank(logoBase64)) {
|
||||
result.append("<image xlink:href=\"" + logoBase64 + "\" height=\"" + logoHeight + "\" width=\"" + logoWidth + "\" y=\"" + logoY + "\" x=\"" + logoX + "\" />\n");
|
||||
}
|
||||
result.append("</svg>");
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -547,8 +605,9 @@ public class QrCodeUtil {
|
||||
int width = bitMatrix.getWidth();
|
||||
int height = bitMatrix.getHeight();
|
||||
|
||||
AnsiElement foreground = Ansi8BitColor.foreground(rgbToAnsi8BitValue(qrConfig.getForeColor()));
|
||||
AnsiElement background = Ansi8BitColor.background(rgbToAnsi8BitValue(qrConfig.getBackColor()));
|
||||
|
||||
AnsiElement foreground = qrConfig.foreColor == null ? null : Ansi8BitColor.foreground(rgbToAnsi8BitValue(qrConfig.foreColor));
|
||||
AnsiElement background = qrConfig.backColor == null ? null : Ansi8BitColor.background(rgbToAnsi8BitValue(qrConfig.backColor));
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i <= height; i += 2) {
|
||||
|
@ -30,7 +30,7 @@ public class QrConfig {
|
||||
/** 长 */
|
||||
protected int height;
|
||||
/** 前景色(二维码颜色) */
|
||||
protected int foreColor = BLACK;
|
||||
protected Integer foreColor = BLACK;
|
||||
/** 背景色,默认白色,null表示透明 */
|
||||
protected Integer backColor = WHITE;
|
||||
/** 边距1~4 */
|
||||
|
@ -4,19 +4,19 @@ import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.img.ImgUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.core.lang.ansi.Ansi8BitColor;
|
||||
import cn.hutool.core.lang.ansi.AnsiEncoder;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.datamatrix.encoder.SymbolShapeHint;
|
||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
@ -41,7 +41,7 @@ public class QrCodeUtilTest {
|
||||
// 背景色透明
|
||||
config.setBackColor(null);
|
||||
config.setErrorCorrection(ErrorCorrectionLevel.H);
|
||||
String path = FileUtil.isWindows() ? "d:/test/qrcodeCustom.png" : "~/Desktop/hutool/qrcodeCustom.png";
|
||||
String path = FileUtil.isWindows() ? "d:/test/qrcodeCustom.txt" : "~/Desktop/hutool/qrcodeCustom.svg";
|
||||
QrCodeUtil.generate("https://hutool.cn/", config, FileUtil.touch(path));
|
||||
}
|
||||
|
||||
@ -120,14 +120,13 @@ public class QrCodeUtilTest {
|
||||
.setMargin(1);
|
||||
String svg = QrCodeUtil.generateAsSvg("https://hutool.cn/", qrConfig);
|
||||
Assert.assertNotNull(svg);
|
||||
FileUtil.writeString(svg, "d:/test/qr.svg", StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateAsciiArtTest() {
|
||||
QrConfig qrConfig = QrConfig.create()
|
||||
.setForeColor(new Color(0,255,0))
|
||||
.setBackColor(new Color(0,0,0))
|
||||
.setForeColor(new Color(255,0,255))
|
||||
.setBackColor(new Color(0,255,0))
|
||||
.setWidth(0)
|
||||
.setHeight(0).setMargin(1);
|
||||
String asciiArt = QrCodeUtil.generateAsAsciiArt("https://hutool.cn/",qrConfig);
|
||||
@ -135,4 +134,33 @@ public class QrCodeUtilTest {
|
||||
System.out.println(asciiArt);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateToFileTest() {
|
||||
QrConfig qrConfig = QrConfig.create()
|
||||
.setForeColor(Color.BLUE)
|
||||
.setBackColor(new Color(0,200,255))
|
||||
.setWidth(0)
|
||||
.setHeight(0).setMargin(1);
|
||||
File qrFile = QrCodeUtil.generate("https://hutool.cn/", qrConfig, FileUtil.touch("d:/test/ascii_art_qr_code.txt"));
|
||||
BufferedReader reader = FileUtil.getReader(qrFile, StandardCharsets.UTF_8);
|
||||
reader.lines().forEach(System.out::println);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateToStreamTest() {
|
||||
QrConfig qrConfig = QrConfig.create()
|
||||
.setForeColor(Color.BLUE)
|
||||
.setBackColor(new Color(0,200,255))
|
||||
.setWidth(0)
|
||||
.setHeight(0).setMargin(1);
|
||||
String filepath = "d:/test/qr_stream_to_txt.txt";
|
||||
try (BufferedOutputStream outputStream = FileUtil.getOutputStream(filepath)) {
|
||||
QrCodeUtil.generate("https://hutool.cn/", qrConfig,"txt", outputStream);
|
||||
}catch (IOException e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
BufferedReader reader = FileUtil.getReader(filepath, StandardCharsets.UTF_8);
|
||||
reader.lines().forEach(System.out::println);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user