QrCodeUtil.java 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. package com.railway.common.utils;
  2. import com.google.zxing.BarcodeFormat;
  3. import com.google.zxing.BinaryBitmap;
  4. import com.google.zxing.DecodeHintType;
  5. import com.google.zxing.EncodeHintType;
  6. import com.google.zxing.MultiFormatReader;
  7. import com.google.zxing.MultiFormatWriter;
  8. import com.google.zxing.Result;
  9. import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
  10. import com.google.zxing.common.BitMatrix;
  11. import com.google.zxing.common.HybridBinarizer;
  12. import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
  13. import java.awt.BasicStroke;
  14. import java.awt.Color;
  15. import java.awt.Font;
  16. import java.awt.FontMetrics;
  17. import java.awt.Graphics;
  18. import java.awt.Graphics2D;
  19. import java.awt.Image;
  20. import java.awt.RenderingHints;
  21. import java.awt.Shape;
  22. import java.awt.font.FontRenderContext;
  23. import java.awt.font.LineMetrics;
  24. import java.awt.geom.RoundRectangle2D;
  25. import java.awt.image.BufferedImage;
  26. import java.io.ByteArrayOutputStream;
  27. import java.io.File;
  28. import java.io.OutputStream;
  29. import java.util.Arrays;
  30. import java.util.Collections;
  31. import java.util.Hashtable;
  32. import java.util.List;
  33. import javax.imageio.ImageIO;
  34. import lombok.extern.slf4j.Slf4j;
  35. import sun.font.FontDesignMetrics;
  36. import sun.misc.BASE64Encoder;
  37. /**
  38. * @author ZhaoMn
  39. */
  40. @Slf4j
  41. public class QrCodeUtil {
  42. private static final String CHARSET = "utf-8";
  43. private static final String FORMAT_NAME = "JPG";
  44. /** 二维码尺寸 */
  45. private static final int QRCODE_SIZE = 300;
  46. /** LOGO宽度 */
  47. private static final int WIDTH = 60;
  48. /** LOGO高度 */
  49. private static final int HEIGHT = 60;
  50. /** 字体大小 */
  51. private static final int FONT_SIZE = 18;
  52. /** 字体高度 */
  53. private static final int FONT_HEIGHT = 30;
  54. private static BufferedImage createImage(String content, List<String> bottomDes, String imgPath,
  55. boolean needCompress) throws Exception {
  56. Hashtable<EncodeHintType, Object> hints = new Hashtable<>();
  57. hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
  58. hints.put(EncodeHintType.CHARACTER_SET, CHARSET);
  59. hints.put(EncodeHintType.MARGIN, 1);
  60. BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE,
  61. QRCODE_SIZE, QRCODE_SIZE, hints);
  62. int width = bitMatrix.getWidth();
  63. int height = bitMatrix.getHeight();
  64. int tempHeight = height;
  65. boolean needDescription = (null != bottomDes && bottomDes.size() > 0);
  66. if (needDescription) {
  67. tempHeight += (FONT_HEIGHT * bottomDes.size());
  68. }
  69. BufferedImage image = new BufferedImage(width, tempHeight, BufferedImage.TYPE_INT_RGB);
  70. for (int x = 0; x < width; x++) {
  71. for (int y = 0; y < height; y++) {
  72. image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
  73. }
  74. }
  75. // 插入图片
  76. if (imgPath != null && !"".equals(imgPath)) {
  77. insertImage(image, imgPath, needCompress);
  78. }
  79. //添加底部文字
  80. if (needDescription) {
  81. addFontImage(image, bottomDes);
  82. }
  83. return image;
  84. }
  85. private static BufferedImage createImage(String content, String bottomDes, String imgPath,
  86. boolean needCompress) throws Exception {
  87. return createImage(content, Collections.singletonList(bottomDes), imgPath, needCompress);
  88. }
  89. /**
  90. * 添加 底部图片文字
  91. *
  92. * @param source 图片源
  93. * @param declareText 文字本文
  94. */
  95. private static void addFontImage(BufferedImage source, List<String> declareText) {
  96. Graphics2D graph = source.createGraphics();
  97. //开启文字抗锯齿
  98. graph.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
  99. RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
  100. for(int i = 0; i < declareText.size(); i ++){
  101. String declare = declareText.get(i);
  102. BufferedImage textImage = strToImage(declare);
  103. int width = textImage.getWidth(null);
  104. int height = textImage.getHeight(null);
  105. int y = QRCODE_SIZE + (declareText.size() * 20) - ((declareText.size() - i - 1) * FONT_HEIGHT);
  106. graph.drawImage(textImage, 0, y, width, height, null);
  107. }
  108. graph.dispose();
  109. }
  110. public static void main(String[] args) {
  111. try {
  112. String qcode = "LKDSLSFD2412341234";
  113. String toolName = "名称";
  114. List<String> bottomDes = Arrays.asList("长春西高铁综合车间", "存放处",
  115. toolName + " - " + "2314321412");
  116. encode(qcode, bottomDes, "D:/" + DateUtils.dateTimeNow() + ".jpg");
  117. } catch (Exception e) {
  118. e.printStackTrace();
  119. }
  120. }
  121. private static BufferedImage strToImage(String str) {
  122. BufferedImage textImage = new BufferedImage(QRCODE_SIZE, FONT_HEIGHT, BufferedImage.TYPE_INT_RGB);
  123. Graphics2D g2 = (Graphics2D) textImage.getGraphics();
  124. //开启文字抗锯齿
  125. g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
  126. RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
  127. g2.setBackground(Color.WHITE);
  128. g2.clearRect(0, 0, QRCODE_SIZE, FONT_HEIGHT);
  129. g2.setPaint(Color.BLACK);
  130. FontRenderContext context = g2.getFontRenderContext();
  131. Font font = new Font("宋体", Font.BOLD, FONT_SIZE);
  132. g2.setFont(font);
  133. LineMetrics lineMetrics = font.getLineMetrics(str, context);
  134. FontMetrics fontMetrics = FontDesignMetrics.getMetrics(font);
  135. float offset = ((float) QRCODE_SIZE - fontMetrics.stringWidth(str)) / 2;
  136. float y =
  137. (FONT_HEIGHT + lineMetrics.getAscent() - lineMetrics.getDescent() - lineMetrics.getLeading())
  138. / 2;
  139. g2.drawString(str, (int) offset, (int) y);
  140. return textImage;
  141. }
  142. private static void insertImage(BufferedImage source, String imgPath, boolean needCompress)
  143. throws Exception {
  144. File file = new File(imgPath);
  145. if (!file.exists()) {
  146. System.err.println("" + imgPath + " 该文件不存在!");
  147. return;
  148. }
  149. Image src = ImageIO.read(new File(imgPath));
  150. int width = src.getWidth(null);
  151. int height = src.getHeight(null);
  152. // 压缩LOGO
  153. if (needCompress) {
  154. if (width > WIDTH) {
  155. width = WIDTH;
  156. }
  157. if (height > HEIGHT) {
  158. height = HEIGHT;
  159. }
  160. Image image = src.getScaledInstance(width, height, Image.SCALE_SMOOTH);
  161. BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
  162. Graphics g = tag.getGraphics();
  163. // 绘制缩小后的图
  164. g.drawImage(image, 0, 0, null);
  165. g.dispose();
  166. src = image;
  167. }
  168. // 插入LOGO
  169. Graphics2D graph = source.createGraphics();
  170. int x = (QRCODE_SIZE - width) / 2;
  171. int y = (QRCODE_SIZE - height) / 2;
  172. graph.drawImage(src, x, y, width, height, null);
  173. Shape shape = new RoundRectangle2D.Float(x, y, width, width, 6, 6);
  174. graph.setStroke(new BasicStroke(3f));
  175. graph.draw(shape);
  176. graph.dispose();
  177. }
  178. public static void encode(String content, String bottomDes, String imgPath, String destPath,
  179. boolean needCompress) throws Exception {
  180. BufferedImage image = createImage(content, bottomDes, imgPath, needCompress);
  181. mkdirs(destPath);
  182. ImageIO.write(image, FORMAT_NAME, new File(destPath));
  183. }
  184. /**
  185. * 获取二维码base64数据,转回图片时前面加上 data:image/png;base64,
  186. */
  187. public static String encodeStr(String content, List<String> bottomDes) throws Exception {
  188. BufferedImage image = createImage(content, bottomDes, null, false);
  189. //io流
  190. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  191. //写入流中
  192. ImageIO.write(image, FORMAT_NAME, baos);
  193. //转换成字节
  194. byte[] bytes = baos.toByteArray();
  195. BASE64Encoder encoder = new BASE64Encoder();
  196. //转换成base64串
  197. String jpgBase64 = encoder.encodeBuffer(bytes).trim();
  198. //删除 \r\n
  199. jpgBase64 = jpgBase64.replaceAll("\n", "").replaceAll("\r", "");
  200. return jpgBase64;
  201. }
  202. public static BufferedImage encode(String content, String bottomDes, String imgPath,
  203. boolean needCompress) throws Exception {
  204. return createImage(content, bottomDes, imgPath, needCompress);
  205. }
  206. public static void mkdirs(String destPath) throws Exception {
  207. File file = new File(destPath);
  208. // 当文件夹不存在时,mkdirs会自动创建多层目录,区别于mkdir.(mkdir如果父目录不存在则会抛出异常)
  209. if (!file.exists() && !file.isDirectory()) {
  210. if(!file.mkdirs()){
  211. throw new Exception(destPath + ", 创建失败");
  212. }
  213. }
  214. }
  215. public static void encode(String content, List<String> bottomDes, String destPath)
  216. throws Exception {
  217. BufferedImage image = createImage(content, bottomDes, destPath, false);
  218. mkdirs(destPath);
  219. ImageIO.write(image, FORMAT_NAME, new File(destPath));
  220. }
  221. public static void encode(String content, String bottomDes, String imgPath, String destPath)
  222. throws Exception {
  223. encode(content, bottomDes, imgPath, destPath, false);
  224. }
  225. public static void encode(String content, String bottomDes, String destPath) throws Exception {
  226. encode(content, bottomDes, null, destPath, false);
  227. }
  228. public static void encode(String content, String bottomDes, String imgPath, OutputStream output,
  229. boolean needCompress) throws Exception {
  230. BufferedImage image = createImage(content, bottomDes, imgPath, needCompress);
  231. ImageIO.write(image, FORMAT_NAME, output);
  232. }
  233. public static void encode(String content, String bottomDes, OutputStream output)
  234. throws Exception {
  235. encode(content, bottomDes, null, output, false);
  236. }
  237. public static String decode(File file) throws Exception {
  238. BufferedImage image;
  239. image = ImageIO.read(file);
  240. if (image == null) {
  241. return null;
  242. }
  243. BufferedImageLuminanceSource source = new BufferedImageLuminanceSource(image);
  244. BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
  245. Result result;
  246. Hashtable<DecodeHintType, Object> hints = new Hashtable<>();
  247. hints.put(DecodeHintType.CHARACTER_SET, CHARSET);
  248. result = new MultiFormatReader().decode(bitmap, hints);
  249. return result.getText();
  250. }
  251. public static String decode(String path) throws Exception {
  252. return decode(new File(path));
  253. }
  254. }