java生成條形碼,使用zxing框架,並去除條碼兩邊空白

ZXing(“zebra crossing”)是一個用Java實現的開源,多格式1D / 2D條形碼圖像處理庫,具有其他語言的端口。

如果不想看源碼分析,想直接看代碼請跳到最後!!!

常用的Code 128 碼與 Code 39 碼比較:

Code 128 碼與 Code 39 碼都廣泛運用在企業內部管理、生產流程、物流控制系統方面。不同的在於 Code 128 比 Code 39 能表現更多的字符,單位長度裏的編碼密度更高。
當單位長度裏不能容下 Code 39 編碼或編碼字符超出了 Code 39 的限制時,就可選擇 Code 128 來編碼。所以 Code 128 比 Code 39 更具靈性。
CODE128碼是1981年引入的一種高密度條碼,CODE128 碼可表示從 ASCII 0 到ASCII 127 共128個字符,故稱128碼。其中包含了數字、字母和符號字符。

以下內容以code128編碼爲例,分析ZXing源碼瞭解原理,先看看頂層調用的代碼:

try {
    // 圖像數據轉換,使用了矩陣轉換 參數順序分別爲:編碼內容,編碼類型,生成圖片寬度,生成圖片高度,設置參數
    BitMatrix bitMatrix = new MultiFormatWriter().encode(contents,
            BarcodeFormat.CODE_128, codeWidth, height, hints);
   return  MatrixToImageWriter.toBufferedImage(bitMatrix);
} catch (Exception e) {
    e.printStackTrace();
}

encode方法有5個參數,hints爲ZXing的參數Map集合,查看源碼瞭解具體參數,先查看MultiFormatWriter的code方法

@Override
  public BitMatrix encode(String contents,
                          BarcodeFormat format,
                          int width, int height,
                          Map<EncodeHintType,?> hints) throws WriterException {

    Writer writer;
    switch (format) {
      case EAN_8:
        writer = new EAN8Writer();
        break;
      case EAN_13:
        writer = new EAN13Writer();
        break;
      case UPC_A:
        writer = new UPCAWriter();
        break;
      case QR_CODE:
        writer = new QRCodeWriter();
        break;
      case CODE_39:
        writer = new Code39Writer();
        break;
      case CODE_128:
        writer = new Code128Writer();
        break;
      case ITF:
        writer = new ITFWriter();
        break;
      case PDF_417:
        writer = new PDF417Writer();
        break;
      case CODABAR:
        writer = new CodaBarWriter();
        break;
      case DATA_MATRIX:
        writer = new DataMatrixWriter();
        break;
      case AZTEC:
        writer = new AztecWriter();
        break;
      default:
        throw new IllegalArgumentException("No encoder available for format " + format);
    }
    return writer.encode(contents, format, width, height, hints);
  }

由上面的源碼可以知道,MultiFormatWriter的encode方法調用了實現Writer接口的Code128Writer實例中的encode方法,繼續查看Code128Writer的encode源碼

@Override
  public BitMatrix encode(String contents,
                          BarcodeFormat format,
                          int width,
                          int height,
                          Map<EncodeHintType,?> hints) throws WriterException {
    if (format != BarcodeFormat.CODE_128) {
      throw new IllegalArgumentException("Can only encode CODE_128, but got " + format);
    }
    return super.encode(contents, format, width, height, hints);
  }

發現Code128Writer的encode調用了父類OneDimensionalCodeWriter的encode方法,繼續找

@Override
  public BitMatrix encode(String contents,
                          BarcodeFormat format,
                          int width,
                          int height,
                          Map<EncodeHintType,?> hints) throws WriterException {
    if (contents.isEmpty()) {
      throw new IllegalArgumentException("Found empty contents");
    }

    if (width < 0 || height < 0) {
      throw new IllegalArgumentException("Negative size is not allowed. Input: "
                                             + width + 'x' + height);
    }

    int sidesMargin = getDefaultMargin();
    if (hints != null) {
      Integer sidesMarginInt = (Integer) hints.get(EncodeHintType.MARGIN);
      if (sidesMarginInt != null) {
        sidesMargin = sidesMarginInt;
      }
    }

    boolean[] code = encode(contents);
    return renderResult(code, width, height, sidesMargin);
  }

注意此處有調用Code128Writer的重載方法boolean[] encode(String contents)返回編碼內容所對應的編碼數組,具體編碼數組生成規則就不貼出來了

int sidesMargin = getDefaultMargin();
if (hints != null) {
  Integer sidesMarginInt = (Integer) hints.get(EncodeHintType.MARGIN);
  if (sidesMarginInt != null) {
    sidesMargin = sidesMarginInt;
  }
}

從上面可以看出,ZXing先自動獲取了一個Margin默認值,然後查看hints參數集合中是否存在參數EncodeHintType.MARGIN,存在則替換默認值

 public int getDefaultMargin() {
    // CodaBar spec requires a side margin to be more than ten times wider than narrow space.
    // This seems like a decent idea for a default for all formats.
    return 10;
 }

通過查看getDefaultMargin()發現默認邊距Margin爲10,但是ZXing並不是只看Margin值來設定邊距,而且參考編碼內容和用戶設定的寬度共同計算的!

 private static BitMatrix renderResult(boolean[] code, int width, int height, int sidesMargin) {
    int inputWidth = code.length;
    // Add quiet zone on both sides.
    int fullWidth = inputWidth + sidesMargin;
    int outputWidth = Math.max(width, fullWidth);
    int outputHeight = Math.max(1, height);

    int multiple = outputWidth / fullWidth;
    int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;

    BitMatrix output = new BitMatrix(outputWidth, outputHeight);
    for (int inputX = 0, outputX = leftPadding; inputX < inputWidth; inputX++, outputX += multiple) {
      if (code[inputX]) {
        output.setRegion(outputX, 0, multiple, outputHeight);
      }
    }
    return output;
  }

分析上面源碼可知:

ZXing 條碼邊距及總寬度-默認計算規則如下

  • sidesMargin: 默認爲10,用戶設置了則爲hints中EncodeHintType.MARGIN的值
  • inputWidth: 條碼根據編碼內容自動生成編碼數組長度(code.length)
  • codeWidth: 用戶自定義的條碼寬度
  • fullWidth: 編碼數組長度 inputWidth + 邊距 sidesMargin
  • outputWidth: codeWidth 與 fullWidth 的最大值
 //放大倍數(取整)
 int multiple = outputWidth / fullWidth;
 //邊距
 int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
 生成條碼長度爲: outputWidth + 2 * leftPadding
 

想生成的條形碼無邊距的話,即leftPadding=0,必須設置EncodeHintType.MARGIN爲0的同時保證用戶給定的寬度爲編碼數組長度的倍數。

編碼數組長度可通過如下計算:

int width = new Code128Writer().encode(contents).length;

即當傳入寬度爲 width * n,且EncodeHintType.MARGIN=0時,則條碼無邊框



以下爲完整代碼:

導入依賴

<!-- Zxing -->
<dependency>
	<groupId>com.google.zxing</groupId>
	<artifactId>javase</artifactId>
	<version>3.2.1</version>
</dependency>
<dependency>
	<groupId>com.google.zxing</groupId>
	<artifactId>core</artifactId>
	<version>3.2.1</version>
</dependency>
<!-- Zxing -->

條形碼生成與解析工具類

package com.framework.utils.pay;

import com.google.zxing.EncodeHintType;
import com.google.zxing.oned.Code128Writer;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.LuminanceSource;
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.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;

/**
 * 條形碼工具,內有生成條形碼,與解析辦法
 * @author bhy
 *
 */
public class BarCodeUtil {
	/**
     * 條形碼編碼
     * 
     * @param contents
	 * @return 
     */
    public static BufferedImage encode(String contents) {

        //配置條碼參數
        Map<EncodeHintType,Object> hints = new HashMap<>();
        //設置條碼兩邊空白邊距爲0,默認爲10,如果寬度不是條碼自動生成寬度的倍數則MARGIN無效
        hints.put(EncodeHintType.MARGIN, 0);

        //爲了無邊距,需設置寬度爲條碼自動生成規則的寬度
        int width = new Code128Writer().encode(contents).length;
        //前端可控制高度,不影響識別
        int height = 70;
        //條碼放大倍數
        int codeMultiples = 1;
        //獲取條碼內容的寬,不含兩邊距,當EncodeHintType.MARGIN爲0時即爲條碼寬度
        int codeWidth = width * codeMultiples;

        /* ZXing 條碼邊距及總寬度-默認計算規則
        codeWidth: 自定義的條碼寬度
        fullWidth: 條碼根據編碼內容自動生成編碼數組長度(new Code128Writer().encode(contents).length)+邊距MARGIN
        outputWidth: codeWidth 與 fullWidth 的最大值
        //放大倍數(取整)
        int multiple = outputWidth / fullWidth;
        //邊距
        int leftPadding = (outputWidth - (inputWidth * multiple)) / 2;
        生成條碼長度爲: outputWidth + 2 * leftPadding
         */

        try {

            // 圖像數據轉換,使用了矩陣轉換 參數順序分別爲:編碼內容,編碼類型,生成圖片寬度,生成圖片高度,設置參數
            BitMatrix bitMatrix = new MultiFormatWriter().encode(contents,
                    BarcodeFormat.CODE_128, codeWidth, height, hints);
//            MatrixToImageWriter.writeToStream(bitMatrix, "png", new FileOutputStream("d:/code39.png"));
           return  MatrixToImageWriter.toBufferedImage(bitMatrix);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
 
    /**
     * 解析條形碼
     * 
     * @param imgPath
     * @return
     */
    public static String decode(String imgPath) {
        BufferedImage image = null;
        Result result = null;
        try {
            image = ImageIO.read(new File(imgPath));
            if (image == null) {
            	throw new RuntimeException("the decode image may be not exists.");
            }
            LuminanceSource source = new BufferedImageLuminanceSource(image);
            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
            result = new MultiFormatReader().decode(bitmap, null);
            return result.getText();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}

調用工具類生成條形碼

package com.controller.pay;

import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.dtt.sett.framework.utils.pay.BarCodeUtil;
/**
 * 生成條形碼
 * @author bhy
 *
 */
@Controller
public class BarCodeController  extends PayBaseController{
	
	/**
	 * 生成條形碼
	 * @return
	 */
	@RequestMapping("/getBarCodeImage")
	public String getBarCodeImage(HttpServletRequest req, HttpServletResponse resp, @RequestParam("paymentCode")String imgcode){
		try {
			BufferedImage buffImg = BarCodeUtil.encode(imgcode);
			// 禁止圖像緩存。
			resp.setHeader("Pragma", "no-cache");
			resp.setHeader("Cache-Control", "no-cache");
			resp.setDateHeader("Expires", 0);
			resp.setContentType("image/jpeg");

			// 將圖像輸出到Servlet輸出流中。
			ServletOutputStream sos = resp.getOutputStream();
			ImageIO.write(buffImg, "jpeg", sos);
			sos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		return null;
	}
}

歡迎訪問本文的個人博客鏈接: https://br-bai.github.io/2019/04/12/Java生成條形碼,使用ZXing框架,並去除條碼兩邊空白/

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