|
|
@@ -0,0 +1,121 @@
|
|
|
+package com.fs.hisStore.utils;
|
|
|
+
|
|
|
+import javax.imageio.ImageIO;
|
|
|
+import java.awt.*;
|
|
|
+import java.awt.image.BufferedImage;
|
|
|
+import java.io.ByteArrayInputStream;
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
+import java.io.IOException;
|
|
|
+
|
|
|
+public class QRCodeImageCleaner {
|
|
|
+
|
|
|
+ // 静态代码块确保在服务器无头模式下也能运行,强烈建议保留
|
|
|
+ static {
|
|
|
+ System.setProperty("java.awt.headless", "true");
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 涂白并添加自定义文字(支持自动换行,用 "\n" 分隔),背景改为圆形
|
|
|
+ *
|
|
|
+ * @param originalBytes 原始图片字节
|
|
|
+ * @param text 显示的文字,如需换行使用 "\n",如:"益寿缘\n大药房"
|
|
|
+ * @return 处理后的字节数组
|
|
|
+ */
|
|
|
+ public static byte[] replaceCenterText(byte[] originalBytes, String text) {
|
|
|
+ if (originalBytes == null || originalBytes.length == 0) {
|
|
|
+ return originalBytes;
|
|
|
+ }
|
|
|
+ if (text == null || text.trim().isEmpty()) {
|
|
|
+ return removeCenterText(originalBytes);
|
|
|
+ }
|
|
|
+
|
|
|
+ try (ByteArrayInputStream bais = new ByteArrayInputStream(originalBytes);
|
|
|
+ ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
|
|
+
|
|
|
+ BufferedImage image = ImageIO.read(bais);
|
|
|
+ if (image == null) return originalBytes;
|
|
|
+
|
|
|
+ int width = image.getWidth();
|
|
|
+ int height = image.getHeight();
|
|
|
+
|
|
|
+ // 1. 涂白区域参数(圆形在视觉上比较显小,此处比例从 0.48 微调为 0.42,更加自然)
|
|
|
+ double coverRatio = 0.42;
|
|
|
+ int minSide = Math.min(width, height);
|
|
|
+ int coverSize = (int) (minSide * coverRatio);
|
|
|
+
|
|
|
+ int x = (width - coverSize) / 2;
|
|
|
+ int y = (height - coverSize) / 2;
|
|
|
+
|
|
|
+ Graphics2D g2d = image.createGraphics();
|
|
|
+ try {
|
|
|
+ // 2. 开启强抗锯齿
|
|
|
+ g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
|
|
+ g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
|
|
+ g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
|
|
|
+
|
|
|
+ // ======= 修改处:将 fillRect 替换为 fillOval =======
|
|
|
+ // 3. 涂白背景(圆形)
|
|
|
+ g2d.setColor(Color.WHITE);
|
|
|
+ g2d.fillOval(x, y, coverSize, coverSize);
|
|
|
+ // ==================================================
|
|
|
+
|
|
|
+ // 4. 分割多行文字
|
|
|
+ String[] lines = text.split("\n");
|
|
|
+ int lineCount = lines.length;
|
|
|
+
|
|
|
+ // 5. 计算动态字号
|
|
|
+ int fontSize;
|
|
|
+ if (lineCount == 1) {
|
|
|
+ fontSize = Math.max(24, coverSize / 5);
|
|
|
+ } else {
|
|
|
+ fontSize = Math.max(18, coverSize / 6);
|
|
|
+ }
|
|
|
+
|
|
|
+ String fontName = "Microsoft YaHei";
|
|
|
+ Font font = new Font(fontName, Font.BOLD, fontSize);
|
|
|
+ if (!font.getFamily().contains(fontName)) {
|
|
|
+ font = new Font("SimHei", Font.BOLD, fontSize);
|
|
|
+ }
|
|
|
+ g2d.setFont(font);
|
|
|
+ FontMetrics fm = g2d.getFontMetrics();
|
|
|
+
|
|
|
+ // 6. 多行文本总高度和行间距
|
|
|
+ int fontHeight = fm.getHeight();
|
|
|
+ int lineSpacing = Math.max(4, fontSize / 3);
|
|
|
+ int totalTextHeight = (lineCount * fontHeight) + (lineCount - 1) * lineSpacing;
|
|
|
+
|
|
|
+ // 7. 垂直居中计算
|
|
|
+ int startY = (height - totalTextHeight) / 2 + fm.getAscent();
|
|
|
+
|
|
|
+ // 8. 逐行绘制
|
|
|
+ for (int i = 0; i < lineCount; i++) {
|
|
|
+ String line = lines[i];
|
|
|
+ int textWidth = fm.stringWidth(line);
|
|
|
+ int textX = (width - textWidth) / 2;
|
|
|
+ int textY = startY + i * (fontHeight + lineSpacing);
|
|
|
+
|
|
|
+ g2d.setColor(new Color(44, 84, 158));
|
|
|
+ g2d.drawString(line, textX, textY);
|
|
|
+ }
|
|
|
+
|
|
|
+ } finally {
|
|
|
+ g2d.dispose();
|
|
|
+ }
|
|
|
+
|
|
|
+ ImageIO.write(image, "png", baos);
|
|
|
+ return baos.toByteArray();
|
|
|
+
|
|
|
+ } catch (IOException e) {
|
|
|
+ System.err.println("二维码图片处理失败: " + e.getMessage());
|
|
|
+ return originalBytes;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 去除二维码中间的文字方法
|
|
|
+ */
|
|
|
+ public static byte[] removeCenterText(byte[] originalBytes) {
|
|
|
+ return replaceCenterText(originalBytes, null);
|
|
|
+ }
|
|
|
+}
|