java生成、识别条形码和二维码

一、概述

  • 使用 zxing 开源库

    • Zxing主要是Google出品的,用于识别一维码和二维码的第三方库
    • 主要类:
    • BitMatrix 位图矩阵
    • MultiFormatWriter 位图编写器
    • MatrixToImageWriter 写入图片
  • 可以生成、识别条形码和二维码

  • 内置三种尺寸:enum Size {SMALL, MIDDLE, BIG}

  • 依赖

<dependency>
    <groupId>com.google.zxing</groupId>
    <artifactId>javase</artifactId>
    <version>3.5.1</version>
</dependency>
<dependency>
    <groupId>com.google.zxing</groupId>
    <artifactId>core</artifactId>
    <version>3.5.1</version>
</dependency>
<!-- web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.6.7</version>
</dependency>
<dependency>
    <groupId>commons-lang</groupId>
    <artifactId>commons-lang</artifactId>
    <version>2.6</version>
</dependency>

二、效果图

三、条形码

  • 将宽度不等的多个黑条和白条,按照一定的编码规则排序,用以表达一组信息的图像标识符
  • 通常代表一串数字 / 字母,每一位有特殊含义
  • 一般数据容量30个数字 / 字母

1、生成简单条形码(无文字)

/**
 * 生成简单条形码(无文字)
 *
 * @param content
 * @param width
 * @param height
 * @return
*/
public static BufferedImage create(String content, int width, int height) {
    // 定义位图矩阵BitMatrix
    BitMatrix matrix = null;

    try {
        // 定义二维码参数
        Map<EncodeHintType, Object> hints = new HashMap<>(3);
        // 设置编码
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        // 设置容错等级
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        // 设置边距,默认为5
        hints.put(EncodeHintType.MARGIN, 3);

        // 使用code_128格式进行编码生成条形码
        MultiFormatWriter writer = new MultiFormatWriter();
        matrix = writer.encode(content, BarcodeFormat.CODE_128, width, height, hints);
    } catch (WriterException e) {
        e.printStackTrace();
        //throw new RuntimeException("条形码内容写入失败");
    }

    return MatrixToImageWriter.toBufferedImage(matrix);
}

2、生成条形码(含文字)

  • 生成条形码(含文字)
/**
 * 生成条形码(含文字)
 * ****************************************************
 * ----------------------------------------------
 * |                         2023-06-10 10:55   |
 * |                                            |
 * |            商品名称 /超出不显示/              |
 * |                                            |
 * |    | || ||| | || |||| | | | ||| | | ||     |
 * |    | || ||| | || |||| | | | ||| | | ||     |
 * |    | || ||| | || |||| | | | ||| | | ||     |
 * |    | || ||| | || |||| | | | ||| | | ||     |
 * |               13800000000                  |
 * ----------------------------------------------
 * ===================================================
 * 1、日期:顶部右侧
 * 2、商品名称:居中
 * 3、条形码:商品名称下方,且居中
 * 4、条码内容:条形码下方,且居中
 * *****************************************************
 *
 * @param codeImage     条形码图片
 * @param bottomStr     底部文字
 * @param titleStr      标题文字
 * @param topRightStr   右上角文字
 * @param pictureWidth  图片宽度
 * @param pictureHeight 图片高度
 * @param margin        边距
 * @param fontSize      字体大小
 * @return 条形码图片
 */
private static BufferedImage createWithWords(
    BufferedImage codeImage,
    String bottomStr,
    String titleStr,
    String topRightStr,
    int pictureWidth,
    int pictureHeight,
    int margin,
    int fontSize) {
    BufferedImage picImage = new BufferedImage(pictureWidth, pictureHeight, BufferedImage.TYPE_INT_RGB);

    Graphics2D g2d = picImage.createGraphics();
    // 抗锯齿
    setGraphics2D(g2d);
    // 设置白色
    setColorWhite(g2d, picImage.getWidth(), picImage.getHeight());

    // 条形码默认居中显示
    int codeStartX = (pictureWidth - codeImage.getWidth()) / 2;
    int codeStartY = (pictureHeight - codeImage.getHeight()) / 2 + 2 * margin;
    // 画条形码到新的面板
    g2d.drawImage(codeImage, codeStartX, codeStartY, codeImage.getWidth(), codeImage.getHeight(), null);

    // 给条码上下各画一条线
    // 设置颜色
    g2d.setColor(Color.LIGHT_GRAY);
    int y1 = 2 * margin + codeImage.getHeight();
    int y2 = 2 * margin + 2 * codeImage.getHeight();
    g2d.drawLine(0, y1, pictureWidth, y1);
    g2d.drawLine(0, y2, pictureWidth, y2);

    // 画文字到新的面板
    g2d.setColor(Color.BLACK);
    // 字体、字型、字号
    g2d.setFont(new Font("微软雅黑", Font.PLAIN, fontSize));
    // 文字与条形码之间的间隔
    int wordAndCodeSpacing = 3;

    // 底部文字(居中)
    if (StringUtils.isNotEmpty(bottomStr)) {
        // 文字长度
        int strWidth = g2d.getFontMetrics().stringWidth(bottomStr);
        // 文字X轴开始坐标
        int strStartX = (pictureWidth - strWidth) / 2;
        if (strStartX < margin) {
            strStartX = margin;
        }
        // 文字Y轴开始坐标
        int strStartY = codeStartY + codeImage.getHeight() + fontSize + wordAndCodeSpacing;
        // 画文字
        g2d.drawString(bottomStr, strStartX, strStartY);
    }

    // 右上角文字
    if (StringUtils.isNotEmpty(topRightStr)) {
        // 文字长度
        int strWidth = g2d.getFontMetrics().stringWidth(topRightStr);
        // 文字X轴开始坐标
        int strStartX = pictureWidth - strWidth - margin;
        // 文字Y轴开始坐标
        int strStartY = margin + fontSize;
        // 画文字
        g2d.drawString(topRightStr, strStartX, strStartY);
    }

    // 标题文字(居中)
    if (StringUtils.isNotEmpty(titleStr)) {
    	if (titleStr.length() > 11) {
            titleStr = titleStr.substring(0, 11);
        }
        // 字体、字型、字号
        int fs = (int) Math.ceil(fontSize * 1.5);
        g2d.setFont(new Font("微软雅黑", Font.PLAIN, fs));
        // 文字长度
        int strWidth = g2d.getFontMetrics().stringWidth(titleStr);
        // 文字X轴开始坐标
        int strStartX = (pictureWidth - strWidth) / 2;
        if (strStartX < margin) {
            strStartX = margin;
        }
        // 文字Y轴开始坐标
        int strStartY = codeStartY - margin;
        // 画文字
        g2d.drawString(titleStr, strStartX, strStartY);
    }

    g2d.dispose();
    picImage.flush();

    return picImage;
}
  • 设置 Graphics2D 属性 (抗锯齿)
/**
 * 设置 Graphics2D 属性 (抗锯齿)
 *
 * @param g2d Graphics2D提供对几何形状、坐标转换、颜色管理和文本布局更为复杂的控制
 */
private static void setGraphics2D(Graphics2D g2d) {
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT);
    Stroke s = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER);
    g2d.setStroke(s);
}
  • 设置背景为白色
/**
 * 设置背景为白色
 *
 * @param g2d Graphics2D提供对几何形状、坐标转换、颜色管理和文本布局更为复杂的控制
 */
private static void setColorWhite(Graphics2D g2d, int width, int height) {
    g2d.setColor(Color.WHITE);
    // 填充整个屏幕
    g2d.fillRect(0, 0, width, height);
    // 设置笔刷
    g2d.setColor(Color.BLACK);
}
  • 创建小号条形码(250x150)
/**
 * 创建小号条形码(250x150)
 *
 * @param code
 * @param name
 * @return
 */
private static BufferedImage createSmallWithWords(@NonNull String code, @NonNull String name) {
    // 条形码底部内容
    String bottomStr = code;
    // 条形码左上角内容
    //String topLeftStr = name.length() < 11 ? name : name.substring(0, 11);
    String topLeftStr = name;
    // 条形码右上角内容
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
    String topRightStr = dtf.format(LocalDateTime.now());

    // 生成条形码
    BufferedImage barCodeImage = create(bottomStr, 250, 50);

    return createWithWords(
        barCodeImage,
        bottomStr,
        topLeftStr,
        topRightStr,
        250,
        150,
        10,
        14);
}
  • 创建中号条形码(350x210)
/**
 * 创建中号条形码(350x210)
 *
 * @param code
 * @param name
 * @return
 */
private static BufferedImage createMiddleWithWords(@NonNull String code, @NonNull String name) {
    // 条形码底部内容
    String bottomStr = code;
    // 条形码左上角内容
    //String topLeftStr = name.length() < 11 ? name : name.substring(0, 11);
    String topLeftStr = name;
    // 条形码右上角内容
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
    String topRightStr = dtf.format(LocalDateTime.now());

    // 生成条形码
    BufferedImage barCodeImage = create(bottomStr, 350, 70);

    return createWithWords(
        barCodeImage,
        bottomStr,
        topLeftStr,
        topRightStr,
        350,
        210,
        14,
        20);
}
  • 创建大号条形码(500x300)
/**
 * 创建大号条形码(500x300)
 *
 * @param code
 * @param name
 * @return
 */
private static BufferedImage createBigWithWords(@NonNull String code, @NonNull String name) {
    // 条形码底部内容
    String bottomStr = code;
    // 条形码左上角内容
    //String topLeftStr = name.length() < 11 ? name : name.substring(0, 11);
    String topLeftStr = name;
    // 条形码右上角内容
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
    String topRightStr = dtf.format(LocalDateTime.now());

    // 生成条形码
    BufferedImage barCodeImage = create(bottomStr, 500, 100);

    return createWithWords(
        barCodeImage,
        bottomStr,
        topLeftStr,
        topRightStr,
        500,
        300,
        20,
        28);
}
  • 创建内置尺寸(小/中/大)条形码
/**
 * 生成条形码(带文字)
 *
 * @param code 编码内容
 * @param name 名称
 * @param size 规格:小中大
 * @return
 */
public static BufferedImage createWithWords(
    @NonNull String code,
    @NonNull String name,
    @NonNull Size size
) {
    switch (size) {
        case SMALL:
            return createSmallWithWords(code, name);
        case MIDDLE:
            return createMiddleWithWords(code, name);
        case BIG:
        default:
            return createBigWithWords(code, name);
    }
}

四、二维码

  • 用某种特定几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息
  • 比一维条形码能存储更多信息,表示更多数据类型
  • 能够存储数字 / 字母 / 汉字 / 图片等信息
  • 可存储几百到几十KB字符

1、生成指定尺寸二维码

/**
 * 生成二维码(正方形)
 * @param content
 * @param sideLength 边长(px)
 * @return
 */
public static BufferedImage create(String content, int sideLength) {
    BitMatrix matrix = null;
    // 定义二维码参数
    Map<EncodeHintType, Object> hints = new HashMap<>(3);
    // 设置编码
    hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
    // 设置容错等级
    hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
    // 设置边距,默认为5
    hints.put(EncodeHintType.MARGIN, 3);

    try {
        matrix = new MultiFormatWriter()
            .encode(content, BarcodeFormat.QR_CODE, sideLength, sideLength, hints);
    } catch (Exception e) {
        e.printStackTrace();
        //throw new RuntimeException("条形码内容写入失败");
    }
    return MatrixToImageWriter.toBufferedImage(matrix);
}

2、生成内置尺寸(小/中/大)二维码

小号:250px;中号:350px;大号:500px;

public static BufferedImage create(String content, Size size) {
    switch (size) {
        case SMALL:
            return create(content, 250);
        case MIDDLE:
            return create(content, 350);
        case BIG:
        default:
            return create(content, 500);
    }
}

五、保存

  • 存入文件
/**
 * 存入文件
 *
 * @param barCodeImage
 * @return 文件名
 */
public static String toFile(BufferedImage barCodeImage) {
    String fileName = getFileName();
    boolean isWrite = false;
    try {
        FileOutputStream outputStream = new FileOutputStream(new File(getImageDir(), fileName));
        isWrite = ImageIO.write(barCodeImage, "png", outputStream);
    } catch (IOException e) {
        e.printStackTrace();
    }

    if (isWrite) {
        return fileName;
    }
    return "";
}
  • 存放目录
/**
 * 得到图片存放目录
 * 类路径下的image文件夹
 *
 * @return
 */
private static String getImageDir() {
    String imageDir = "";
    Resource resource = new ClassPathResource("");
    try {
        String _dir = resource.getFile().getAbsolutePath();
        File file = new File(_dir, "image");
        if (!file.exists()) {
            file.mkdirs();
        }
        imageDir = file.getAbsolutePath();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return imageDir;
}
  • 文件名
/**
 * 生成文件名
 * 当前时间戳 + 两位随机数
 *
 * @return
 */
private static String getFileName() {
    // 当前时间戳
    long second = Instant.now().getEpochSecond();

    // 两位随机数
    ThreadLocalRandom r = ThreadLocalRandom.current();
    int rand = r.nextInt(10, 99);

    return new StringBuffer().append(second).append(rand).append(".png").toString();
}

六、识别

/**
 * 识别图形码(条形码/二维码)
 * ***********************************************
 * ------ 条形码 ----------------------------------
 * 可以识别内容:手机号、纯数字、字母数字、网址(中大号)
 * 识别不了小号(250x150)含文字的网址条形码
 * 存储网址时用二维码,不要使用条形码
 * ***********************************************
 * ------ 二维码 ----------------------------------
 * 比条形码有优势;能识别三种尺寸的网址二维码
 * ***********************************************
 *
 * @param imageCode 图形码文件
 * @return void
 */
public static String scan(File imageCode) {
    String text = "";
    try {
        BufferedImage image = ImageIO.read(imageCode);
        if (image == null) {
            return text;
        }
        LuminanceSource source = new BufferedImageLuminanceSource(image);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));

        Map<DecodeHintType, Object> hints = new HashMap<>(3);
        hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
        hints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE);
        hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
        Result result = new MultiFormatReader().decode(bitmap, hints);
        text = result.getText();
    } catch (Exception e) {
        e.printStackTrace();
        System.out.println("无法识别");
    }
    return text;
}

public static String scan(String fileName) {
    return scan(new File(getImageDir(), fileName));
}

public static String scan(String fileName, String fileParent) {
    return scan(new File(fileParent, fileName));
}

七、测试类

package com.tuwer.util;

/**
 * <p>描述...</p>
 *
 * @author 土味儿
 * @version 1.0
 * @Date 2023/6/10
 */
public class ImageCodeTest {
    public static void main(String[] args) {
        cr();
        //sc();
    }

    /**
     * 创建
     */
    private static void cr() {
        //String code = "13800000000";
        String code = "1667320863285510145";
        //String code = "abcdefg123456789012";
        //String code = "http://www.baidu.com";
        String title = "商品名称(超出不显示)";
        System.out.println(ImageCodeUtil.toFile(ImageCodeUtil.BarCode.createWithWords(code, title, ImageCodeUtil.Size.SMALL)));
        System.out.println(ImageCodeUtil.toFile(ImageCodeUtil.BarCode.createWithWords(code, title, ImageCodeUtil.Size.MIDDLE)));
        System.out.println(ImageCodeUtil.toFile(ImageCodeUtil.BarCode.createWithWords(code, title, ImageCodeUtil.Size.BIG)));

        //System.out.println(toFile(QrCode.create(code, 500)));
        //System.out.println(ImageCodeUtil.toFile(ImageCodeUtil.QrCode.create(code, ImageCodeUtil.Size.SMALL)));
        //System.out.println(ImageCodeUtil.toFile(ImageCodeUtil.QrCode.create(code, ImageCodeUtil.Size.MIDDLE)));
        //System.out.println(ImageCodeUtil.toFile(ImageCodeUtil.QrCode.create(code, ImageCodeUtil.Size.BIG)));
    }

    /**
     * 识别
     */
    private static void sc() {
        // 扫描
        // 13800000000
        //String[] fs = {"168636218096.png", "168636218180.png", "168636218127.png"};

        // 1667320863285510145
        //String[] fs = {"168636260651.png", "168636260766.png", "168636260794.png"};

        // abcdefg123456789012
        //String[] fs = {"168636320566.png", "168636320750.png", "168636320743.png"};

        // http://www.baidu.com
        String[] fs = {"168636738387.png", "168636738545.png", "168636738539.png"};

        for (String f : fs) {
            System.out.println("-----------------");
            System.out.println(ImageCodeUtil.scan(f));
        }
    }
}

八、完整源码

package com.tuwer.util;

import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import org.apache.commons.lang.StringUtils;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.lang.NonNull;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;

/**
 * <p>图形码工具类</p>
 *
 * @author 土味儿
 * @version 1.0
 * @Date 2023/6/9
 */
public class ImageCodeUtil {
    /**
     * 尺寸
     */
    enum Size {SMALL, MIDDLE, BIG}

    // ====================== 条形码内部类 ========================

    /**
     * 条形码
     */
    public static class BarCode {
        /**
         * 生成简单条形码(无文字)
         *
         * @param content
         * @param width
         * @param height
         * @return
         */
        public static BufferedImage create(String content, int width, int height) {
            // 定义位图矩阵BitMatrix
            BitMatrix matrix = null;

            try {
                // 定义二维码参数
                Map<EncodeHintType, Object> hints = new HashMap<>(3);
                // 设置编码
                hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
                // 设置容错等级
                hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
                // 设置边距,默认为5
                hints.put(EncodeHintType.MARGIN, 3);

                // 使用code_128格式进行编码生成条形码
                MultiFormatWriter writer = new MultiFormatWriter();
                matrix = writer.encode(content, BarcodeFormat.CODE_128, width, height, hints);
            } catch (WriterException e) {
                e.printStackTrace();
                //throw new RuntimeException("条形码内容写入失败");
            }

            return MatrixToImageWriter.toBufferedImage(matrix);
        }

        /**
         * 生成条形码(含文字)
         * ****************************************************
         * ----------------------------------------------
         * |                         2023-06-10 10:55   |
         * |                                            |
         * |            商品名称 /超出不显示/              |
         * |                                            |
         * |    | || ||| | || |||| | | | ||| | | ||     |
         * |    | || ||| | || |||| | | | ||| | | ||     |
         * |    | || ||| | || |||| | | | ||| | | ||     |
         * |    | || ||| | || |||| | | | ||| | | ||     |
         * |               13800000000                  |
         * ----------------------------------------------
         * ===================================================
         * 1、日期:顶部右侧
         * 2、商品名称:居中
         * 3、条形码:商品名称下方,且居中
         * 4、条码内容:条形码下方,且居中
         * *****************************************************
         *
         * @param codeImage     条形码图片
         * @param bottomStr     底部文字
         * @param titleStr      标题文字
         * @param topRightStr   右上角文字
         * @param pictureWidth  图片宽度
         * @param pictureHeight 图片高度
         * @param margin        边距
         * @param fontSize      字体大小
         * @return 条形码图片
         */
        private static BufferedImage createWithWords(
                BufferedImage codeImage,
                String bottomStr,
                String titleStr,
                String topRightStr,
                int pictureWidth,
                int pictureHeight,
                int margin,
                int fontSize) {
            BufferedImage picImage = new BufferedImage(pictureWidth, pictureHeight, BufferedImage.TYPE_INT_RGB);

            Graphics2D g2d = picImage.createGraphics();
            // 抗锯齿
            setGraphics2D(g2d);
            // 设置白色
            setColorWhite(g2d, picImage.getWidth(), picImage.getHeight());

            // 条形码默认居中显示
            int codeStartX = (pictureWidth - codeImage.getWidth()) / 2;
            int codeStartY = (pictureHeight - codeImage.getHeight()) / 2 + 2 * margin;
            // 画条形码到新的面板
            g2d.drawImage(codeImage, codeStartX, codeStartY, codeImage.getWidth(), codeImage.getHeight(), null);

            // 给条码上下各画一条线
            // 设置颜色
            g2d.setColor(Color.LIGHT_GRAY);
            int y1 = 2 * margin + codeImage.getHeight();
            int y2 = 2 * margin + 2 * codeImage.getHeight();
            g2d.drawLine(0, y1, pictureWidth, y1);
            g2d.drawLine(0, y2, pictureWidth, y2);

            // 画文字到新的面板
            g2d.setColor(Color.BLACK);
            // 字体、字型、字号
            g2d.setFont(new Font("微软雅黑", Font.PLAIN, fontSize));
            // 文字与条形码之间的间隔
            int wordAndCodeSpacing = 3;

            // 底部文字(居中)
            if (StringUtils.isNotEmpty(bottomStr)) {
                // 文字长度
                int strWidth = g2d.getFontMetrics().stringWidth(bottomStr);
                // 文字X轴开始坐标
                int strStartX = (pictureWidth - strWidth) / 2;
                if (strStartX < margin) {
                    strStartX = margin;
                }
                // 文字Y轴开始坐标
                int strStartY = codeStartY + codeImage.getHeight() + fontSize + wordAndCodeSpacing;
                // 画文字
                g2d.drawString(bottomStr, strStartX, strStartY);
            }

            // 右上角文字
            if (StringUtils.isNotEmpty(topRightStr)) {
                // 文字长度
                int strWidth = g2d.getFontMetrics().stringWidth(topRightStr);
                // 文字X轴开始坐标
                int strStartX = pictureWidth - strWidth - margin;
                // 文字Y轴开始坐标
                int strStartY = margin + fontSize;
                // 画文字
                g2d.drawString(topRightStr, strStartX, strStartY);
            }

            // 标题文字(居中)
            if (StringUtils.isNotEmpty(titleStr)) {
                // 字体、字型、字号
                int fs = (int) Math.ceil(fontSize * 1.5);
                g2d.setFont(new Font("微软雅黑", Font.PLAIN, fs));
                // 文字长度
                int strWidth = g2d.getFontMetrics().stringWidth(titleStr);
                // 文字X轴开始坐标
                int strStartX = (pictureWidth - strWidth) / 2;
                if (strStartX < margin) {
                    strStartX = margin;
                }
                // 文字Y轴开始坐标
                int strStartY = codeStartY - margin;
                // 画文字
                g2d.drawString(titleStr, strStartX, strStartY);
            }

            g2d.dispose();
            picImage.flush();

            return picImage;
        }

        /**
         * 设置 Graphics2D 属性 (抗锯齿)
         *
         * @param g2d Graphics2D提供对几何形状、坐标转换、颜色管理和文本布局更为复杂的控制
         */
        private static void setGraphics2D(Graphics2D g2d) {
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_DEFAULT);
            Stroke s = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER);
            g2d.setStroke(s);
        }

        /**
         * 设置背景为白色
         *
         * @param g2d Graphics2D提供对几何形状、坐标转换、颜色管理和文本布局更为复杂的控制
         */
        private static void setColorWhite(Graphics2D g2d, int width, int height) {
            g2d.setColor(Color.WHITE);
            // 填充整个屏幕
            g2d.fillRect(0, 0, width, height);
            // 设置笔刷
            g2d.setColor(Color.BLACK);
        }

        /**
         * 创建小号条形码(250x150)
         *
         * @param code
         * @param name
         * @return
         */
        private static BufferedImage createSmallWithWords(@NonNull String code, @NonNull String name) {
            // 条形码底部内容
            String bottomStr = code;
            // 条形码左上角内容
            //String topLeftStr = name.length() < 11 ? name : name.substring(0, 11);
            String topLeftStr = name;
            // 条形码右上角内容
            DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
            String topRightStr = dtf.format(LocalDateTime.now());

            // 生成条形码
            BufferedImage barCodeImage = create(bottomStr, 250, 50);

            return createWithWords(
                    barCodeImage,
                    bottomStr,
                    topLeftStr,
                    topRightStr,
                    250,
                    150,
                    10,
                    14);
        }

        /**
         * 创建中号条形码(350x210)
         *
         * @param code
         * @param name
         * @return
         */
        private static BufferedImage createMiddleWithWords(@NonNull String code, @NonNull String name) {
            // 条形码底部内容
            String bottomStr = code;
            // 条形码左上角内容
            //String topLeftStr = name.length() < 11 ? name : name.substring(0, 11);
            String topLeftStr = name;
            // 条形码右上角内容
            DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
            String topRightStr = dtf.format(LocalDateTime.now());

            // 生成条形码
            BufferedImage barCodeImage = create(bottomStr, 350, 70);

            return createWithWords(
                    barCodeImage,
                    bottomStr,
                    topLeftStr,
                    topRightStr,
                    350,
                    210,
                    14,
                    20);
        }

        /**
         * 创建大号条形码(500x300)
         *
         * @param code
         * @param name
         * @return
         */
        private static BufferedImage createBigWithWords(@NonNull String code, @NonNull String name) {
            // 条形码底部内容
            String bottomStr = code;
            // 条形码左上角内容
            //String topLeftStr = name.length() < 11 ? name : name.substring(0, 11);
            String topLeftStr = name;
            // 条形码右上角内容
            DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
            String topRightStr = dtf.format(LocalDateTime.now());

            // 生成条形码
            BufferedImage barCodeImage = create(bottomStr, 500, 100);

            return createWithWords(
                    barCodeImage,
                    bottomStr,
                    topLeftStr,
                    topRightStr,
                    500,
                    300,
                    20,
                    28);
        }

        /**
         * 生成条形码(带文字)
         *
         * @param code 编码内容
         * @param name 名称
         * @param size 规格:小中大
         * @return
         */
        public static BufferedImage createWithWords(
                @NonNull String code,
                @NonNull String name,
                @NonNull Size size
        ) {
            switch (size) {
                case SMALL:
                    return createSmallWithWords(code, name);
                case MIDDLE:
                    return createMiddleWithWords(code, name);
                case BIG:
                default:
                    return createBigWithWords(code, name);
            }
        }
    }

    // ====================== 二维码内部类 ========================

    /**
     * 二维码
     */
    public static class QrCode {
        /**
         * 生成二维码(正方形)
         *
         * @param content
         * @param sideLength 边长(px)
         * @return
         */
        public static BufferedImage create(String content, int sideLength) {
            BitMatrix matrix = null;
            // 定义二维码参数
            Map<EncodeHintType, Object> hints = new HashMap<>(3);
            // 设置编码
            hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
            // 设置容错等级
            hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
            // 设置边距,默认为5
            hints.put(EncodeHintType.MARGIN, 3);

            try {
                matrix = new MultiFormatWriter()
                        .encode(content, BarcodeFormat.QR_CODE, sideLength, sideLength, hints);
            } catch (Exception e) {
                e.printStackTrace();
                //throw new RuntimeException("条形码内容写入失败");
            }
            return MatrixToImageWriter.toBufferedImage(matrix);
        }

        public static BufferedImage create(String content, Size size) {
            switch (size) {
                case SMALL:
                    return create(content, 250);
                case MIDDLE:
                    return create(content, 350);
                case BIG:
                default:
                    return create(content, 500);
            }
        }
    }

    // ======================== 存入文件 =========================

    /**
     * 存入文件
     *
     * @param barCodeImage
     * @return 文件名
     */
    public static String toFile(BufferedImage barCodeImage) {
        String fileName = getFileName();
        boolean isWrite = false;
        try {
            FileOutputStream outputStream = new FileOutputStream(new File(getImageDir(), fileName));
            isWrite = ImageIO.write(barCodeImage, "png", outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

        if (isWrite) {
            return fileName;
        }
        return "";
    }

    /**
     * 得到图片存放目录
     * 类路径下的image文件夹
     *
     * @return
     */
    private static String getImageDir() {
        String imageDir = "";
        Resource resource = new ClassPathResource("");
        try {
            String _dir = resource.getFile().getAbsolutePath();
            File file = new File(_dir, "image");
            if (!file.exists()) {
                file.mkdirs();
            }
            imageDir = file.getAbsolutePath();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return imageDir;
    }

    /**
     * 生成文件名
     * 当前时间戳 + 两位随机数
     *
     * @return
     */
    private static String getFileName() {
        // 当前时间戳
        long second = Instant.now().getEpochSecond();

        // 两位随机数
        ThreadLocalRandom r = ThreadLocalRandom.current();
        int rand = r.nextInt(10, 99);

        return new StringBuffer().append(second).append(rand).append(".png").toString();
    }

    // ======================= 识别图形码 ========================

    /**
     * 识别图形码(条形码/二维码)
     * ***********************************************
     * ------ 条形码 ----------------------------------
     * 可以识别内容:手机号、纯数字、字母数字、网址(中大号)
     * 识别不了小号(250x150)含文字的网址条形码
     * 存储网址时用二维码,不要使用条形码
     * ***********************************************
     * ------ 二维码 ----------------------------------
     * 比条形码有优势;能识别三种尺寸的网址二维码
     * ***********************************************
     *
     * @param imageCode 图形码文件
     * @return void
     */
    public static String scan(File imageCode) {
        String text = "";
        try {
            BufferedImage image = ImageIO.read(imageCode);
            if (image == null) {
                return text;
            }
            LuminanceSource source = new BufferedImageLuminanceSource(image);
            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));

            Map<DecodeHintType, Object> hints = new HashMap<>(3);
            hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
            hints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE);
            hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
            Result result = new MultiFormatReader().decode(bitmap, hints);
            text = result.getText();
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("无法识别");
        }
        return text;
    }

    public static String scan(String fileName) {
        return scan(new File(getImageDir(), fileName));
    }

    public static String scan(String fileName, String fileParent) {
        return scan(new File(fileParent, fileName));
    }
}

参考自:Java生成读取条形码及二维码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/27527.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

华为OD机试真题 JavaScript 实现【求符合要求的结对方式】【2023Q1 100分】,附详细解题思路

一、题目描述 用一个数组A代表程序员的工作能力&#xff0c;公司想通过结对编程的方式提高员工的能力&#xff0c;假设结对后的能力为两个员工的能力之和&#xff0c;求一共有多少种结对方式使结对后能力为N。 二、输入描述 6 2 3 3 4 5 1 6 第一行为员工的总人数&#xff…

计算机视觉-目标检测(一):从 R-CNN 到 Faster R-CNN

文章目录 1. 概要2. 区域卷积卷积神经网络R-CNN2.1 模型结构2.2 Selective Search2.3 warp2.4 R-CNN训练2.5 R-CNN推理2.6 R-CNN性能评价2.7 R-CNN的缺点 3. SPP-Net3.1 SPP-Net对RCNN的改进3.2 SPP-Net网络结构3.3 SPP-Net训练过程3.4 SPP-Net的问题 4. Fast R-CNN4.1 Fast R-…

河北沃克HEGERLS仓储货架生产厂家|夹抱式伸缩货叉四向穿梭车新型物流机器人

众所周知仓库作业主要是围绕存取、搬运、拣选、输送分拣而进行的&#xff0c;而随着物流作业的多样化、复杂化&#xff0c;四向穿梭车作为新的存储技术&#xff0c;以其灵活、柔性等特点而备受瞩目。河北沃克在成功研发四向穿梭车的基础上又对其进行了产品的横向发展。目前&…

微服务_fegin

Feign服务调用 是客户端组件 ruoyi系统中Log\Auth\User用了远程服务调用&#xff0c;用工厂模式给他的报错加了层工厂类&#xff0c;return错误的时候重写了以下方法。 在ruoyi-common-core模块中引入依赖 <!-- SpringCloud Openfeign --><dependency><group…

python语法-数据可视化(全球GDP动态柱状图开发)

python数据可视化&#xff08;全球GDP动态柱状图开发&#xff09; 开发工具&#xff1a;pycharm、pyecharts模块 &#xff08;项目数据见文末参考内容&#xff09; """ 演示GDP动态柱状图开发 """from pyecharts.charts import Bar,Timeline fr…

ExtractOfficeContent: 提取Office文件中文本、表格和图像

引言 最近有空写了一下这个库&#xff0c;用来提取Office文件中的文本和图像内容&#xff0c;用作后续整理训练语料使用。最新更新请移步&#xff1a;Github Extract Office Content Use Installextract_office_content$ pip install extract_office_contentRun by CLI. Ext…

数据可视化大屏人员停留系统的开发实录(默认加载条件筛选、单击加载、自动刷新加载、异步加载数据)

项目需求 录入进入房间的相关数据&#xff1b;从进入时间开始计时&#xff0c;计算滞留房间的时间&#xff1b;定时刷新数据&#xff0c;超过30分钟的人数&#xff0c;进行红色告警&#xff1b; 实现流程 为了完整地实现上述需求&#xff0c;我们可以按照以下步骤开发&#…

JAVA面向对象(三)

第三章 封装与继承 目录 第三章 封装与继承 1.1.封装 1.2.包 1.3.访问权限控制 1.4.static修饰符 1.4.1.成员变量 1.4.2.成员方法 1.4.3.代码块 总结 内容仅供学习交流&#xff0c;如有问题请留言或私信&#xff01;&#xff01;&#xff01;&#xff01;&#xff0…

【计算机组成与体系结构Ⅰ】知识点整理

第一章 计算机系统概论 1.1 从源文件到可执行文件 .c源程序、.i源程序、.s汇编语言程序、.o可重定位目标程序、可执行目标程序&#xff1b;后两个为二进制&#xff0c;前面为文本 1.2 可执行文件的启动和执行 冯诺依曼结构计算机模型的5大基本部件&#xff1a;运算器、控制…

技巧:win10的另一种美化字体的方式,使用noMeiryoUI

目录 1. 前提2. 字体选择3. 查看已经安装的字体并查看效果4. 安装软件修改系统字体5. 修改浏览器字体 1. 前提 21年的时候写了一篇文章&#xff0c;《Windows10下美化字体&#xff0c;达到类似mac的效果》&#xff0c;当时还很迷恋macType这个软件的使用&#xff0c;觉得好牛逼…

java异常 | 处理规范、全局异常、Error处理

文章目录 &#x1f683;异常类型&#x1f3a0;显示声明异常&#xff1a;①&#xff1a;try-catch②方法签名 &#x1f683;异常处理规范⚓️异常包装⚓️异常传递⚓️异常日志记录⚓️异常处理的最佳实践 &#x1f683;全局异常处理⛵️优点&#xff1a;⛵️代码示例&#xff1…

YOLOv7训练自定义数据集

使用YOLOv7做对比实验&#xff0c;需要重新部署一下YOLO环境&#xff0c;并将COCO格式数据集转换为YOLO格式 博主的COCO数据集是由WiderPerson数据集转换来的&#xff0c;并且做了一些处理。 环境 Ubuntu18.0 CUDA11.2 NVIDIA T4 项目部署 下载项目&#xff1a; git clone…

PureComponent和Component的区别和底层处理机制

PureComponent和Component都是React中的组件类&#xff0c;但它们在实现细节和使用上有些差别。 Component是React中定义组件的基类&#xff0c;它的shouldComponentUpdate方法默认返回true&#xff0c;也就是说&#xff0c;每次调用setState或forceUpdate方法都会引发组件重新…

算法提高-图论-单源最短路的综合应用

单源最短路的综合应用 单源最短路的综合应用AcWing 1135. 新年好AcWing 340. 通信线路AcWing 342. 道路与航线AcWing 341. 最优贸易 单源最短路的综合应用 AcWing 1135. 新年好 多次dijkstra求每个点到其它点的最短距离&#xff0c; 此时相当于建好了一张图&#xff0c;每个点…

实验篇(7.2) 09. 通过安全隧道走对方宽带上网 (FortiClient-IPsec) ❀ 远程访问

【简介】要想所有的流量都走安全隧道&#xff0c;就需要禁用隧道分割。这样上网流量也会通过隧道到达远端防火墙&#xff0c;再通过远端防火墙的宽带接口去到互联网。我们来看看FortiClient客户端用IPsec VPN是如何实现的。 实验要求与环境 OldMei集团深圳总部防火墙有两条宽带…

【运筹优化】最短路算法之A星算法 + Java代码实现

文章目录 一、A星算法简介二、A星算法思想三、A星算法 java代码四、测试 一、A星算法简介 A*算法是一种静态路网中求解最短路径最有效的直接搜索方法&#xff0c;也是解决许多搜索问题的有效算法。算法中的距离估算值与实际值越接近&#xff0c;最终搜索速度越快。 二、A星算…

javaScript蓝桥杯-----天气趋势 A

目录 一、介绍二、准备三、目标四、代码五、完成 一、介绍 日常生活中&#xff0c;气象数据对于人们的生活具有非常重要的意义&#xff0c;数据的表现形式多种多样&#xff0c;使用图表进行展示使数据在呈现上更加直观。 本题请实现一个 Y 城 2022 年的天气趋势图。 二、准备…

100天精通Python(可视化篇)——第88天:全网最全Seaborn库常用绘图3万字总结(参数说明+案例实战)

文章目录 一、Seaborn介绍1.1 介绍1.2 安装1.3 风格设置1.3.1 style&#xff08;风格&#xff09;1.3.2 context&#xff08;环境设置&#xff09; 1.4 调色盘设置1.5 数据集下载 二、Relational plots&#xff08;关系图&#xff09;2.1 scatterplot&#xff08;散点图&#x…

SpringSecurity 总结

SpringSecurity 总结 第一章 权限管理 权限管理SpringSecurity 简介整体架构 权限管理&#xff1a; 实现: "对用户访问系统的控制"(身份认证) &#xff0c; 按照 "安全规则"或者 "安全策略" (对已经认证的用户进行授权) 控制&#xff0c;用…

K8s in Action 阅读笔记——【13】Securing cluster nodes and the network

K8s in Action 阅读笔记——【13】Securing cluster nodes and the network 13.1 Using the host node’s namespaces in a pod Pod中的容器通常在不同的Linux名称空间下运行&#xff0c;这使得它们的进程与其他容器或节点默认名称空间下运行的进程隔离开来。 例如&#xff…