package com.railway.common.utils; import com.google.zxing.BarcodeFormat; import com.google.zxing.BinaryBitmap; import com.google.zxing.DecodeHintType; import com.google.zxing.EncodeHintType; import com.google.zxing.MultiFormatReader; import com.google.zxing.MultiFormatWriter; import com.google.zxing.Result; import com.google.zxing.client.j2se.BufferedImageLuminanceSource; import com.google.zxing.common.BitMatrix; import com.google.zxing.common.HybridBinarizer; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.font.FontRenderContext; import java.awt.font.LineMetrics; import java.awt.geom.RoundRectangle2D; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.OutputStream; import java.util.Arrays; import java.util.Collections; import java.util.Hashtable; import java.util.List; import javax.imageio.ImageIO; import lombok.extern.slf4j.Slf4j; import sun.font.FontDesignMetrics; import sun.misc.BASE64Encoder; /** * @author ZhaoMn */ @Slf4j public class QrCodeUtil { private static final String CHARSET = "utf-8"; private static final String FORMAT_NAME = "JPG"; /** 二维码尺寸 */ private static final int QRCODE_SIZE = 300; /** LOGO宽度 */ private static final int WIDTH = 60; /** LOGO高度 */ private static final int HEIGHT = 60; /** 字体大小 */ private static final int FONT_SIZE = 18; /** 字体高度 */ private static final int FONT_HEIGHT = 30; private static BufferedImage createImage(String content, List bottomDes, String imgPath, boolean needCompress) throws Exception { Hashtable hints = new Hashtable<>(); hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); hints.put(EncodeHintType.CHARACTER_SET, CHARSET); hints.put(EncodeHintType.MARGIN, 1); BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints); int width = bitMatrix.getWidth(); int height = bitMatrix.getHeight(); int tempHeight = height; boolean needDescription = (null != bottomDes && bottomDes.size() > 0); if (needDescription) { tempHeight += (FONT_HEIGHT * bottomDes.size()); } BufferedImage image = new BufferedImage(width, tempHeight, BufferedImage.TYPE_INT_RGB); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF); } } // 插入图片 if (imgPath != null && !"".equals(imgPath)) { insertImage(image, imgPath, needCompress); } //添加底部文字 if (needDescription) { addFontImage(image, bottomDes); } return image; } private static BufferedImage createImage(String content, String bottomDes, String imgPath, boolean needCompress) throws Exception { return createImage(content, Collections.singletonList(bottomDes), imgPath, needCompress); } /** * 添加 底部图片文字 * * @param source 图片源 * @param declareText 文字本文 */ private static void addFontImage(BufferedImage source, List declareText) { Graphics2D graph = source.createGraphics(); //开启文字抗锯齿 graph.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); for(int i = 0; i < declareText.size(); i ++){ String declare = declareText.get(i); BufferedImage textImage = strToImage(declare); int width = textImage.getWidth(null); int height = textImage.getHeight(null); int y = QRCODE_SIZE + (declareText.size() * 20) - ((declareText.size() - i - 1) * FONT_HEIGHT); graph.drawImage(textImage, 0, y, width, height, null); } graph.dispose(); } public static void main(String[] args) { try { String qcode = "LKDSLSFD2412341234"; String toolName = "名称"; List bottomDes = Arrays.asList("长春西高铁综合车间", "存放处", toolName + " - " + "2314321412"); encode(qcode, bottomDes, "D:/" + DateUtils.dateTimeNow() + ".jpg"); } catch (Exception e) { e.printStackTrace(); } } private static BufferedImage strToImage(String str) { BufferedImage textImage = new BufferedImage(QRCODE_SIZE, FONT_HEIGHT, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = (Graphics2D) textImage.getGraphics(); //开启文字抗锯齿 g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2.setBackground(Color.WHITE); g2.clearRect(0, 0, QRCODE_SIZE, FONT_HEIGHT); g2.setPaint(Color.BLACK); FontRenderContext context = g2.getFontRenderContext(); Font font = new Font("宋体", Font.BOLD, FONT_SIZE); g2.setFont(font); LineMetrics lineMetrics = font.getLineMetrics(str, context); FontMetrics fontMetrics = FontDesignMetrics.getMetrics(font); float offset = ((float) QRCODE_SIZE - fontMetrics.stringWidth(str)) / 2; float y = (FONT_HEIGHT + lineMetrics.getAscent() - lineMetrics.getDescent() - lineMetrics.getLeading()) / 2; g2.drawString(str, (int) offset, (int) y); return textImage; } private static void insertImage(BufferedImage source, String imgPath, boolean needCompress) throws Exception { File file = new File(imgPath); if (!file.exists()) { System.err.println("" + imgPath + " 该文件不存在!"); return; } Image src = ImageIO.read(new File(imgPath)); int width = src.getWidth(null); int height = src.getHeight(null); // 压缩LOGO if (needCompress) { if (width > WIDTH) { width = WIDTH; } if (height > HEIGHT) { height = HEIGHT; } Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH); BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics g = tag.getGraphics(); // 绘制缩小后的图 g.drawImage(image, 0, 0, null); g.dispose(); src = image; } // 插入LOGO Graphics2D graph = source.createGraphics(); int x = (QRCODE_SIZE - width) / 2; int y = (QRCODE_SIZE - height) / 2; graph.drawImage(src, x, y, width, height, null); Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6); graph.setStroke(new BasicStroke(3f)); graph.draw(shape); graph.dispose(); } public static void encode(String content, String bottomDes, String imgPath, String destPath, boolean needCompress) throws Exception { BufferedImage image = createImage(content, bottomDes, imgPath, needCompress); mkdirs(destPath); ImageIO.write(image, FORMAT_NAME, new File(destPath)); } /** * 获取二维码base64数据,转回图片时前面加上 data:image/png;base64, */ public static String encodeStr(String content, List bottomDes) throws Exception { BufferedImage image = createImage(content, bottomDes, null, false); //io流 ByteArrayOutputStream baos = new ByteArrayOutputStream(); //写入流中 ImageIO.write(image, FORMAT_NAME, baos); //转换成字节 byte[] bytes = baos.toByteArray(); BASE64Encoder encoder = new BASE64Encoder(); //转换成base64串 String jpgBase64 = encoder.encodeBuffer(bytes).trim(); //删除 \r\n jpgBase64 = jpgBase64.replaceAll("\n", "").replaceAll("\r", ""); return jpgBase64; } public static BufferedImage encode(String content, String bottomDes, String imgPath, boolean needCompress) throws Exception { return createImage(content, bottomDes, imgPath, needCompress); } public static void mkdirs(String destPath) throws Exception { File file = new File(destPath); // 当文件夹不存在时,mkdirs会自动创建多层目录,区别于mkdir.(mkdir如果父目录不存在则会抛出异常) if (!file.exists() && !file.isDirectory()) { if(!file.mkdirs()){ throw new Exception(destPath + ", 创建失败"); } } } public static void encode(String content, List bottomDes, String destPath) throws Exception { BufferedImage image = createImage(content, bottomDes, destPath, false); mkdirs(destPath); ImageIO.write(image, FORMAT_NAME, new File(destPath)); } public static void encode(String content, String bottomDes, String imgPath, String destPath) throws Exception { encode(content, bottomDes, imgPath, destPath, false); } public static void encode(String content, String bottomDes, String destPath) throws Exception { encode(content, bottomDes, null, destPath, false); } public static void encode(String content, String bottomDes, String imgPath, OutputStream output, boolean needCompress) throws Exception { BufferedImage image = createImage(content, bottomDes, imgPath, needCompress); ImageIO.write(image, FORMAT_NAME, output); } public static void encode(String content, String bottomDes, OutputStream output) throws Exception { encode(content, bottomDes, null, output, false); } public static String decode(File file) throws Exception { BufferedImage image; image = ImageIO.read(file); if (image == null) { return null; } BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); Result result; Hashtable hints = new Hashtable<>(); hints.put(DecodeHintType.CHARACTER_SET, CHARSET); result = new MultiFormatReader().decode(bitmap, hints); return result.getText(); } public static String decode(String path) throws Exception { return decode(new File(path)); } }