From 89dd871af7d95e0cd8a49027815745ffe898dffc Mon Sep 17 00:00:00 2001 From: Looly Date: Sun, 5 Jan 2025 23:10:48 +0800 Subject: [PATCH] add BitMatrixRender --- .../hutool/swing/qrcode/QrAsciiArt.java | 74 ---------- .../hutool/swing/qrcode/QrCodeUtil.java | 60 ++++---- .../dromara/hutool/swing/qrcode/QrConfig.java | 4 +- .../dromara/hutool/swing/qrcode/QrImage.java | 101 ------------- .../dromara/hutool/swing/qrcode/QrSVG.java | 107 -------------- .../swing/qrcode/render/AsciiArtRender.java | 91 ++++++++++++ .../swing/qrcode/render/BitMatrixRender.java | 38 +++++ .../swing/qrcode/render/ImageRender.java | 135 ++++++++++++++++++ .../hutool/swing/qrcode/render/SVGRender.java | 129 +++++++++++++++++ .../swing/qrcode/render/package-info.java | 23 +++ 10 files changed, 442 insertions(+), 320 deletions(-) delete mode 100644 hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/QrAsciiArt.java delete mode 100644 hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/QrImage.java delete mode 100644 hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/QrSVG.java create mode 100644 hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/render/AsciiArtRender.java create mode 100644 hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/render/BitMatrixRender.java create mode 100644 hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/render/ImageRender.java create mode 100644 hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/render/SVGRender.java create mode 100644 hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/render/package-info.java diff --git a/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/QrAsciiArt.java b/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/QrAsciiArt.java deleted file mode 100644 index 5029f1081..000000000 --- a/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/QrAsciiArt.java +++ /dev/null @@ -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(); - } -} diff --git a/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/QrCodeUtil.java b/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/QrCodeUtil.java index 5b8cf738d..0dcb07325 100644 --- a/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/QrCodeUtil.java +++ b/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/QrCodeUtil.java @@ -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 diff --git a/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/QrConfig.java b/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/QrConfig.java index 2522a606f..4d67d5592 100644 --- a/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/QrConfig.java +++ b/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/QrConfig.java @@ -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; } diff --git a/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/QrImage.java b/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/QrImage.java deleted file mode 100644 index e2d0f3f71..000000000 --- a/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/QrImage.java +++ /dev/null @@ -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//不透明 - ); - } - } -} diff --git a/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/QrSVG.java b/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/QrSVG.java deleted file mode 100644 index 9ff50442a..000000000 --- a/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/QrSVG.java +++ /dev/null @@ -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("\n"); - result.append(" \n"); - if (StrUtil.isNotBlank(logoBase64)) { - result.append("\n"); - } - result.append(""); - return result.toString(); - } -} diff --git a/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/render/AsciiArtRender.java b/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/render/AsciiArtRender.java new file mode 100644 index 000000000..e75573fd2 --- /dev/null +++ b/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/render/AsciiArtRender.java @@ -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); + } + } +} diff --git a/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/render/BitMatrixRender.java b/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/render/BitMatrixRender.java new file mode 100644 index 000000000..efaa0fc9a --- /dev/null +++ b/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/render/BitMatrixRender.java @@ -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); +} diff --git a/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/render/ImageRender.java b/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/render/ImageRender.java new file mode 100644 index 000000000..f64788ff4 --- /dev/null +++ b/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/render/ImageRender.java @@ -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//不透明 + ); + } +} diff --git a/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/render/SVGRender.java b/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/render/SVGRender.java new file mode 100644 index 000000000..41cdcea75 --- /dev/null +++ b/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/render/SVGRender.java @@ -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("\n"); + writer.append(" \n"); + if (StrUtil.isNotBlank(logoBase64)) { + writer.append("\n"); + } + writer.append(""); + } catch (final IOException e) { + throw new IORuntimeException(e); + } + } +} diff --git a/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/render/package-info.java b/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/render/package-info.java new file mode 100644 index 000000000..6da56e8e2 --- /dev/null +++ b/hutool-swing/src/main/java/org/dromara/hutool/swing/qrcode/render/package-info.java @@ -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;