二維碼(Logo)工具類

這是整理之後的二維碼生成、解析工具類,並且可以生成含logo的二維碼;使用的是Google的zxing工具包。在理解代碼時可以把我寫的多重重載注掉,不然代碼太長不利於理解。細的不多說,看代碼:

import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Shape;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Hashtable;

import javax.imageio.ImageIO;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.EncodeHintType;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.NotFoundException;
import com.google.zxing.Result;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

/***
 * <p><strong>二維碼工具類</strong></p>
 * 功能實現:<br>
 * 1)生成不同格式(bmp/jpg/jpeg/gif/png)的二維碼,
 *      並且可以生成包含logo的二維碼,
 *      也可以生成彩色的二維碼(此工具類默認採用utf-8編碼)<br>
 *      (注:不能使用白色作爲填充色,會導致無法解析,使用此方法時需要注意測試生成後的二維碼是否可用)<br>
 * 2)解析二維碼(可以是圖片文件,也可以是圖片數據流)
 */
public class QRCodeUtil {
    private static final int BLANK = 0xFF000000;//RGB黑色對應值
    private static final int WHITE = 0xFFFFFFFF;//RGB白色對應值
    private static final String CHARTSET = "utf-8";//
    //圖片格式
    private static final String PNG = "png";
    private static final String JPG = "jpg";
    private static final String GIF = "gif";
    private static final String JPEG = "jpeg";
    private static final String BMP = "bmp";
    //默認值
    private static final int QRCODE_SIZE = 300;
    private static final int LOGO_SIZE = 60;

    /**
     * 生成二維碼編碼,字符集默認採用utf-8
     * @param content 二維碼內容(可以是url,也可以是文本)
     * @param width 生成二維碼的長度
     * @param height 生成二維碼的高度
     * @return WriterException
     * @version 1.0.0
     */
    public BitMatrix encode(String content, int width, int height) throws WriterException {
        //用於設置QR二維碼參數 
        Hashtable<EncodeHintType, Object> hints = new Hashtable<>();
        // 設置QR二維碼的糾錯級別——這裏選擇最高H級別(暫時未涉及此部分內容,不設置也行,出於後續優化考慮,放在這裏) 
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        //設置編碼方式,此工具類默認採用utf-8(後續升級可以考慮支持多種編碼)
        hints.put(EncodeHintType.CHARACTER_SET, CHARTSET);
        //告訴系統二維碼掃描後的內容,以及採用的編碼形式,生成圖片的大小;
        //參數順序分別爲:編碼內容,編碼類型,生成圖片寬度,生成圖片高度,設置參數  
        BitMatrix bitMatrix = new MultiFormatWriter().encode(content, 
                BarcodeFormat.QR_CODE, width, height, hints);
        return bitMatrix;
    }

    /**
     * 生成默認大小的二維碼編碼,字符集默認採用utf-8
     * @param content 二維碼內容(可以是url,也可以是文本)
     */
    public BitMatrix encode(String content) throws WriterException {
        return encode(content, QRCODE_SIZE, QRCODE_SIZE);
    }

    /**
     * <p>創建文件<p>
     * 如果存儲路徑不存在則會創建文件
     * @param storePath 存儲路徑
     * @param fileName 文件名 (如果文件名包含該文件格式後綴會去重)
     * @param format 文件格式
     * @version 1.0.0
     */
    public File mkdirs(String storePath, String fileName, String format) {
        File file = new File(storePath);
        if(!file.exists() && !file.isDirectory()) {
            file.mkdirs();
        }
        if(fileName.contains("." + format)) {
            return new File(storePath + File.separator + fileName);
        }
        return new File(storePath + File.separator + fileName 
                + "." + format.toLowerCase());
    }

    /**
     * 將生成的二維碼轉換成RBG類型的圖片
     * @param bitMatrix 二維碼編碼
     * @param fillColor 填充部分的RGB顏色編碼
     * @param blankColor 空白部分的RBG顏色編碼
     */
    public BufferedImage toBufferedImage(BitMatrix bitMatrix, int fillColor, 
            int blankColor) {
        int width = bitMatrix.getWidth();
        int height = bitMatrix.getHeight();
        BufferedImage image = new BufferedImage(width, height,
                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) ? fillColor : blankColor);
            }
        }
        return image;
    }

    /**
     * 生成默認顏色樣式的二維碼(RBG黑白)
     * @param bitMatrix 二維碼編碼
     */
    public BufferedImage toBufferedImage(BitMatrix bitMatrix) {
        return toBufferedImage(bitMatrix, BLANK, WHITE);
    }

    /**
     * 將二維碼圖片數據寫入文件(輸出)
     * @param image 二維碼圖片數據
     * @param format 圖片格式
     * @param file 將要寫入的文件
     * @throws IOException 
     */
    public void writeToFile(BufferedImage image, String format, File file) 
            throws IOException {
        if (!ImageIO.write(image, format, file)) {
            throw new IOException("Could not write an image of format "
                    + format + " to " + file);
        }
    }

    /**
     * 將二維碼圖片數據寫入文件(輸出)
     * @param image 二維碼圖片數據
     * @param format 圖片格式
     * @param stream 輸出流
     * @throws IOException 
     */
    public void writeToStream(BufferedImage image, String format, 
            OutputStream stream) throws IOException {
        if (!ImageIO.write(image, format, stream)) {
            throw new IOException("Could not write an image of format "
                    + format);
        }
    }

    /**
     * 類型一:創建二維碼,可以設定二維碼的尺寸,顏色,格式,命名,存儲路徑
     */
    public void createQRcode(String content, String format, int width, int height, 
            String storePath, String fileName, int fillColor, int blankColor) 
                    throws WriterException, IOException {
        writeToFile(toBufferedImage(encode(content, width, height), 
                fillColor, blankColor), format, mkdirs(storePath, fileName, format));
    }

    /**
     * 類型二:創建二維碼,可以設定二維碼的尺寸,格式,命名,存儲路徑(顏色默認RGB黑白)
     */
    public void createQRcode(String content, String format, int width, int height, 
            String storePath, String fileName) 
                    throws WriterException, IOException {
        createQRcode(content, format, width, height, storePath, fileName, BLANK, WHITE);
    }

    /**
     * 類型三:創建二維碼,可以設定二維碼的格式,長度(長和寬等值),命名,存儲路徑(顏色默認RGB黑白)
     */
    public void createQRcode(String content, String format, int length, 
            String storePath, String fileName) throws WriterException, IOException {
        createQRcode(content, format, length, length, storePath, fileName);
    }

    /**
     * 類型四:創建二維碼,可以設定二維碼的格式,命名,存儲路徑(顏色默認RGB黑白,尺寸默認300*300)
     */
    public void createQRcode(String content, String format,String storePath, String fileName) 
                    throws WriterException, IOException {
        createQRcode(content, format, QRCODE_SIZE, storePath, fileName);
    }

    /**
     * 類型五:創建二維碼,可以設定二維碼的命名,存儲路徑(顏色默認RGB黑白,尺寸默認300*300,格式爲jpg)
     */
    public void createQRcode(String content,String storePath, String fileName) 
                    throws WriterException, IOException {
        createQRcode(content, JPG, storePath, fileName);
    }

    /**
     * PNG類型一:創建二維碼,可以設定二維碼的尺寸,顏色,格式,命名,存儲路徑
     */
    public void createPngQRcode(String content, int width, int height, 
            String storePath, String fileName, int fillColor, int blankColor) 
                    throws WriterException, IOException {
        createQRcode(content, PNG, width, height, storePath, fileName, fillColor, blankColor);
    }

    /**
     * PNG類型二:創建二維碼,可以設定二維碼的尺寸,格式,命名,存儲路徑(顏色默認RGB黑白)
     */
    public void createPngQRcode(String content, int width, int height, 
            String storePath, String fileName) 
                    throws WriterException, IOException {
        createPngQRcode(content, width, height, storePath, fileName, BLANK, WHITE);
    }

    /**
     * PNG類型三:創建二維碼,可以設定二維碼的格式,長度(長和寬等值),命名,存儲路徑(顏色默認RGB黑白)
     */
    public void createPngQRcode(String content, int length, 
            String storePath, String fileName) throws WriterException, IOException {
        createPngQRcode(content, length, length, storePath, fileName);
    }

    /**
     * PNG類型四:創建二維碼,可以設定二維碼的格式,命名,存儲路徑(顏色默認RGB黑白,尺寸默認300*300)
     */
    public void createPngQRcode(String content,String storePath, String fileName) 
                    throws WriterException, IOException {
        createPngQRcode(content, QRCODE_SIZE, storePath, fileName);
    }

    /**
     * JPG類型一:創建二維碼,可以設定二維碼的尺寸,顏色,格式,命名,存儲路徑
     */
    public void createJpgQRcode(String content, int width, int height, 
            String storePath, String fileName, int fillColor, int blankColor) 
                    throws WriterException, IOException {
        createQRcode(content, JPG, width, height, storePath, fileName, fillColor, blankColor);
    }

    /**
     * JPG類型二:創建二維碼,可以設定二維碼的尺寸,格式,命名,存儲路徑(顏色默認RGB黑白)
     */
    public void createJpgQRcode(String content, int width, int height, 
            String storePath, String fileName) 
                    throws WriterException, IOException {
        createJpgQRcode(content, width, height, storePath, fileName, BLANK, WHITE);
    }

    /**
     * JPG類型三:創建二維碼,可以設定二維碼的格式,長度(長和寬等值),命名,存儲路徑(顏色默認RGB黑白)
     */
    public void createJpgQRcode(String content, int length, 
            String storePath, String fileName) throws WriterException, IOException {
        createJpgQRcode(content, length, length, storePath, fileName);
    }

    /**
     * JPG類型四:創建二維碼,可以設定二維碼的格式,命名,存儲路徑(顏色默認RGB黑白,尺寸默認300*300)
     */
    public void createJpgQRcode(String content, String storePath, String fileName) 
                    throws WriterException, IOException {
        createJpgQRcode(content, QRCODE_SIZE, storePath, fileName);
    }

    /**
     * JPEG類型一:創建二維碼,可以設定二維碼的尺寸,顏色,格式,命名,存儲路徑
     */
    public void createJpegQRcode(String content, int width, int height, 
            String storePath, String fileName, int fillColor, int blankColor) 
                    throws WriterException, IOException {
        createQRcode(content, JPEG, width, height, storePath, fileName, fillColor, blankColor);
    }

    /**
     * JPEG類型二:創建二維碼,可以設定二維碼的尺寸,格式,命名,存儲路徑(顏色默認RGB黑白)
     */
    public void createJpegQRcode(String content, int width, int height, 
            String storePath, String fileName) 
                    throws WriterException, IOException {
        createJpegQRcode(content, width, height, storePath, fileName, BLANK, WHITE);
    }

    /**
     * JPEG類型三:創建二維碼,可以設定二維碼的格式,長度(長和寬等值),命名,存儲路徑(顏色默認RGB黑白)
     */
    public void createJpegQRcode(String content, int length, 
            String storePath, String fileName) throws WriterException, IOException {
        createJpegQRcode(content, length, length, storePath, fileName);
    }

    /**
     * JPEG類型四:創建二維碼,可以設定二維碼的格式,命名,存儲路徑(顏色默認RGB黑白,尺寸默認300*300)
     */
    public void createJpegQRcode(String content,String storePath, String fileName) 
                    throws WriterException, IOException {
        createJpegQRcode(content, QRCODE_SIZE, storePath, fileName);
    }

    /**
     * GIF類型一:創建二維碼,可以設定二維碼的尺寸,顏色,格式,命名,存儲路徑
     */
    public void createGifQRcode(String content, int width, int height, 
            String storePath, String fileName, int fillColor, int blankColor) 
                    throws WriterException, IOException {
        createQRcode(content, GIF, width, height, storePath, fileName, fillColor, blankColor);
    }

    /**
     * GIF類型二:創建二維碼,可以設定二維碼的尺寸,格式,命名,存儲路徑(顏色默認RGB黑白)
     */
    public void createGifQRcode(String content, int width, int height, 
            String storePath, String fileName) 
                    throws WriterException, IOException {
        createGifQRcode(content, width, height, storePath, fileName, BLANK, WHITE);
    }

    /**
     * GIF類型三:創建二維碼,可以設定二維碼的格式,長度(長和寬等值),命名,存儲路徑(顏色默認RGB黑白)
     */
    public void createGifQRcode(String content, int length, 
            String storePath, String fileName) throws WriterException, IOException {
        createGifQRcode(content, length, length, storePath, fileName);
    }

    /**
     * GIF類型四:創建二維碼,可以設定二維碼的格式,命名,存儲路徑(顏色默認RGB黑白,尺寸默認300*300)
     */
    public void createGifQRcode(String content, String storePath, String fileName) 
                    throws WriterException, IOException {
        createGifQRcode(content, QRCODE_SIZE, storePath, fileName);
    }

    /**
     * BMP類型一:創建二維碼,可以設定二維碼的尺寸,顏色,格式,命名,存儲路徑
     */
    public void createBmpQRcode(String content, int width, int height, 
            String storePath, String fileName, int fillColor, int blankColor) 
                    throws WriterException, IOException {
        createQRcode(content, BMP, width, height, storePath, fileName, fillColor, blankColor);
    }

    /**
     * BMP類型二:創建二維碼,可以設定二維碼的尺寸,格式,命名,存儲路徑(顏色默認RGB黑白)
     */
    public void createBmpQRcode(String content, int width, int height, 
            String storePath, String fileName) 
                    throws WriterException, IOException {
        createBmpQRcode(content, width, height, storePath, fileName, BLANK, WHITE);
    }

    /**
     * BMP類型三:創建二維碼,可以設定二維碼的格式,長度(長和寬等值),命名,存儲路徑(顏色默認RGB黑白)
     */
    public void createBmpQRcode(String content, int length, 
            String storePath, String fileName) throws WriterException, IOException {
        createBmpQRcode(content, length, length, storePath, fileName);
    }

    /**
     * BMP類型四:創建二維碼,可以設定二維碼的格式,命名,存儲路徑(顏色默認RGB黑白,尺寸默認300*300)
     */
    public void createBmpQRcode(String content, String storePath, String fileName) 
                    throws WriterException, IOException {
        createBmpQRcode(content, QRCODE_SIZE, storePath, fileName);
    }

    /**
     * 向二維碼圖片中插入logo圖片
     * @param source 二維碼圖片流
     * @param imgPath logo圖片路徑
     * @param needCompress 是否需要壓縮
     */
    public BufferedImage insertImage(BufferedImage source, String imgPath,
            boolean needCompress) throws IOException {
        File file = new File(imgPath);
        if(!file.exists()) {
            throw new FileNotFoundException("Could not find the file or the "
                    + "file does not exist : " + imgPath);
        }
        Image image = ImageIO.read(file);
        int width = image.getWidth(null);
        int height = image.getHeight(null);
        if(needCompress) {//壓縮(這裏是固定大小壓縮,可以修改成可選大小)
            width = LOGO_SIZE;
            height = LOGO_SIZE;
            Image img = image.getScaledInstance(width, height, Image.SCALE_SMOOTH);
            BufferedImage target = new BufferedImage(width, height,
                    BufferedImage.TYPE_INT_RGB);
            Graphics g = target.getGraphics();
            g.drawImage(img, 0, 0, null);
            image = img;
        }
        Graphics2D g2D = source.createGraphics();
        int x = (source.getWidth() - width) / 2;
        int y = (source.getHeight() - width) / 2;
        g2D.drawImage(image, x, y, width, height, null);
        Shape shape = new RoundRectangle2D.Float(x, y, width, height, 6, 6);
        g2D.setStroke(new BasicStroke(3f));
        g2D.draw(shape);
        g2D.dispose();
        return source;
    }

    public void createLogoQRcode(String content, String format, int width, int height, 
            String storePath, String fileName, int fillColor, int blankColor, String logoPath) 
                    throws WriterException, IOException {
        writeToFile(insertImage(toBufferedImage(encode(content, width, height), 
                fillColor, blankColor), logoPath, true), format, mkdirs(storePath, fileName, format));
    }

    public void createLogoQRcode(String content, String format,String storePath, 
            String fileName, int fillColor, int blankColor, String logoPath)
                    throws WriterException, IOException {
        createLogoQRcode(content, format, QRCODE_SIZE, QRCODE_SIZE, storePath,
                fileName, fillColor, blankColor, logoPath);
    }

    public void createLogoQRcode(String content, String format,String storePath, int length,
            String fileName, int fillColor, int blankColor, String logoPath)
                    throws WriterException, IOException {
        createLogoQRcode(content, format, length, length, storePath,
                fileName, fillColor, blankColor, logoPath);
    }

    public void createLogoQRcode(String content, String format,String storePath,
            String fileName, String logoPath)throws WriterException, IOException {
        createLogoQRcode(content, format, storePath, fileName, BLANK, WHITE, logoPath);
    }

    public void createLogoQRcode(String content, String storePath,
            String fileName, String logoPath)throws WriterException, IOException {
        createLogoQRcode(content, JPG, storePath, fileName, BLANK, WHITE, logoPath);
    }

    /**
     * 通過二維碼文件流解析二維碼
     */
    public String analyticalQRCode(BufferedImage image) throws NotFoundException{
        LuminanceSource source = new BufferedImageLuminanceSource(image);
        BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
        Result result;
        Hashtable<DecodeHintType, String> hints = new Hashtable<DecodeHintType, String>();
        //解碼設置編碼方式:utf-8
        hints.put(DecodeHintType.CHARACTER_SET, CHARTSET);
        result = new MultiFormatReader().decode(bitmap, hints);
        return result.getText();
    }

    /**
     * 通過二維碼文件解析二維碼
     * @param file 二維碼文件
     */
    public String analyticalQRCode(File file) throws IOException, NotFoundException {
        BufferedImage image = ImageIO.read(file);
        if (image == null) {
            throw new FileNotFoundException("can't found file or it's null");
        }
        return analyticalQRCode(image);
    }

    /**
     * 通過二維碼路徑解析二維碼
     * @param path 二維碼路徑
     */
    public String analyticalQRCode(String path) throws IOException, NotFoundException {
        File file = new File(path);
        if(!file.exists()) {
            throw new FileNotFoundException("Could not find the file or the "
                    + "file does not exist : " + path);
        }
        return analyticalQRCode(file);
    }

    public static void main(String[] args) {
        int grey = 0xFFBEBEBE;
        String logo = "E:/360downloads/wpcache/360wallpaper.jpg";
        try {
            new QRCodeUtil().createQRcode("https://www.baidu.com", JPG, 300, 300, "E:", "code", BLANK, WHITE);
            new QRCodeUtil().createJpgQRcode("https://www.baidu.com", "E:/", "code1");
            new QRCodeUtil().createLogoQRcode("https://www.baidu.com", JPG, 300, 300, "E:", "code2", BLANK, grey, logo);
            System.out.println(new QRCodeUtil().analyticalQRCode(new File("E:/code.jpg")));
        } catch (WriterException | IOException e) {
            e.printStackTrace();
        } catch (NotFoundException e) {
            e.printStackTrace();
        }
    }
}

其中引入的BufferedImageLuminanceSource的代碼如下:

import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;

import com.google.zxing.LuminanceSource;

/**
 * LuminanceSource實現類
 * 
 */
public class BufferedImageLuminanceSource extends LuminanceSource {
    /**
     * BufferedImage是Image的一個子類,BufferedImage生成的圖片在內存裏有一個圖像緩衝區,
     * 利用這個緩衝區我們可以很方便的操作這個圖片,通常用來做圖片修改操作如大小變換、圖片變灰、設置圖片透明或不透明等。
     */
    private final BufferedImage image;
    private final int left;
    private final int top;

    /**
     * 構造方法
     * 
     * @param image
     */
    public BufferedImageLuminanceSource(BufferedImage image) {
        this(image, 0, 0, image.getWidth(), image.getHeight());
    }

    /**
     * 重載的構造方法
     * 
     * @param image
     * @param left
     * @param top
     * @param width
     * @param height
     */
    public BufferedImageLuminanceSource(BufferedImage image, int left, int top,
            int width, int height) {
        super(width, height);

        /**
         * 取出當前圖片的寬度和高度
         */
        int sourceWidth = image.getWidth();
        int sourceHeight = image.getHeight();

        if (left + width > sourceWidth || top + height > sourceHeight) {
            throw new IllegalArgumentException(
                    "Crop rectangle does not fit within image data.");
        }

        for (int y = top; y < top + height; y++) {
            for (int x = left; x < left + width; x++) {
                if ((image.getRGB(x, y) & 0xFF000000) == 0) {
                    image.setRGB(x, y, 0xFFFFFFFF); // = white
                }
            }
        }

        this.image = new BufferedImage(sourceWidth, sourceHeight,
                BufferedImage.TYPE_BYTE_GRAY);
        this.image.getGraphics().drawImage(image, 0, 0, null);
        this.left = left;
        this.top = top;
    }

    @Override
    public byte[] getRow(int y, byte[] row) {
        if (y < 0 || y >= getHeight()) {
            throw new IllegalArgumentException(
                    "Requested row is outside the image: " + y);
        }
        int width = getWidth();
        if (row == null || row.length < width) {
            row = new byte[width];
        }
        image.getRaster().getDataElements(left, top + y, width, 1, row);
        return row;
    }

    @Override
    public byte[] getMatrix() {
        int width = getWidth();
        int height = getHeight();
        int area = width * height;
        byte[] matrix = new byte[area];
        image.getRaster().getDataElements(left, top, width, height, matrix);
        return matrix;
    }

    @Override
    public boolean isCropSupported() {
        return true;
    }

    @Override
    public LuminanceSource crop(int left, int top, int width, int height) {
        return new BufferedImageLuminanceSource(image, this.left + left,
                this.top + top, width, height);
    }

    @Override
    public boolean isRotateSupported() {
        return true;
    }

    @Override
    public LuminanceSource rotateCounterClockwise() {

        int sourceWidth = image.getWidth();
        int sourceHeight = image.getHeight();

        AffineTransform transform = new AffineTransform(0.0, -1.0, 1.0, 0.0,
                0.0, sourceWidth);

        BufferedImage rotatedImage = new BufferedImage(sourceHeight,
                sourceWidth, BufferedImage.TYPE_BYTE_GRAY);

        Graphics2D g = rotatedImage.createGraphics();
        g.drawImage(image, transform, null);
        g.dispose();

        int width = getWidth();
        return new BufferedImageLuminanceSource(rotatedImage, top, sourceWidth
                - (left + width), getHeight(), width);
    }
}

如果你的導入你的項目中出現警告時,那說明你的項目沒有權限訪問JDK中的一些類,這個時候你需要先remove path中的JDK,然後在添加JDK,雖然沒有意義的操作但是你可以獲得JDK的全部訪問權限

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章