條形碼輸出到pdf打印,封裝工具類

最近項目需要用到條形碼,對於資產進行管理追蹤,所以相應的就需要用到生成條形碼的工具,在網上查找了相關資料之後,目前有幾類產品,google 的zxing 、jbarcode、barcode4j-light 等,他們的區別我不再說,很多人有寫,最終我選擇的是jbarcode,選擇這個是因爲這個jar 可以生成中文文字等等,像一些博客所寫的那樣,開始寫的時候,我也是想把文字直接繪製在條形碼圖片中,但是繪製之後發現效果不理想,調整輸出條形碼圖片更改它的寬度等等都不太方便,所以最後還是選擇直接繪製出條形碼,不加任何文字,然後將繪製出的圖片輸出流直接寫入pdf中,因爲條形碼打印需要特定的標籤打印機,所以對於紙張的大小我們需要調整pdf頁面的大小,而不是選擇A4之類的紙張填寫內容。所以,綜合以上想法,我們的操作步驟是:

1、繪製條形碼圖片

2、將條形碼圖片輸出流輸入進pdf頁面表格中

3、導出pdf

效果如下:

這裏我們需要引入jbarcode.jar(繪製條形碼),jbarcode在maven上沒有,所以需要手動下載jar包,然後導入自己本地maven倉庫引用。

導入:打開cmd ,輸入以下命令即可,只需要更改你下載jbarcode後存放的地址即可

mvn install:install-file -Dfile="D:\Apache Maven\jbarcode-0.2.8.jar" -DgroupId=org.jbarcode -DartifactId=jbarcode -Dversion=0.2.8 -Dpackaging=jar

另外需要引入itext、itext-asian ,這個是操作pdf的

maven依賴:

<properties>
     <jbarcode.version>0.2.8</jbarcode.version>
     <iText.version>4.2.1</iText.version>
     <iTextAsian.version>5.2.0</iTextAsian.version>
</properties>

<!--引入條形碼生成工具-->
<dependency>
     <groupId>org.jbarcode</groupId>
     <artifactId>jbarcode</artifactId>
     <version>${jbarcode.version}</version>
</dependency>

<!-- pdf操作類-->
<dependency>
     <groupId>com.lowagie</groupId>
     <artifactId>iText</artifactId>
     <version>${iText.version}</version>
</dependency>

<dependency>
     <groupId>com.itextpdf</groupId>
     <artifactId>itext-asian</artifactId>
     <version>${iTextAsian.version}</version>
</dependency>

關於如何操作pdf,網上有很多資料可以借鑑

http://www.anyrt.com/blog/list/itextpdf.html#1

不過需要注意:這裏面有一個我遇到的很奇葩的bug

插圖圖片時:

PdfPCell cell = new PdfPCell();
byte[] bt = barCodeImgStream.toByteArray();
com.lowagie.text.Image image = Image.getInstance(bt);
cell.setImage(image);

如果是先new一個單元格,然後裝入圖片,那麼對於之前你設置的img圖片的樣式全部失效,需要注意

正確操作是:

byte[] bt = barCodeImgStream.toByteArray();
com.lowagie.text.Image image = Image.getInstance(bt);
image.scaleAbsoluteHeight(imgHeight);//設置圖片的高
image.scaleAbsoluteWidth(imgWidth);//設置圖片的寬
//重點,必須將設置的圖片在單元格new時放進單元格,而不是後續cell.setImg(),不然對於圖片的設置會失效
PdfPCell cell = new PdfPCell(image);

直接在new單元格時將圖片對象放進去,這樣圖片之前的樣式設置纔會生效,這個問題真的鬱悶了我好久

 

直接上代碼了:不廢話了:想起來了,我用了lombox,小夥伴記得加上這個插件,這個插件是用來省去我們寫get、set的

package com.xxx.common.utils;

import com.lowagie.text.Image;
import com.lowagie.text.Rectangle;
import com.lowagie.text.*;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfPCell;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfWriter;
import com.xxx.common.core.domain.AjaxResult;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.jbarcode.JBarcode;
import org.jbarcode.JBarcodeFactory;
import org.jbarcode.encode.Code128Encoder;
import org.jbarcode.paint.TextPainter;
import org.jbarcode.util.ImageUtil;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *  
 *  * @projectName xxx
 *  * @title     BarCode2PdfUtil   
 *  * @package    com.xxx.common.utils  
 *  * @description    條形碼序列號導出條形碼至pdf
 *  * @author IT_CREAT     
 *  * @date  2019 2019/10/1 16:47  
 *  * @version V1.0.0 
 *  
 */
@Data
@NoArgsConstructor
public class BarCode2PdfUtil {

    // 設置條形碼默認分辨率
    public static final int BARCODE_DPI = ImageUtil.DEFAULT_DPI;

    /**條形碼字節輸出流*/
    public List<ByteArrayOutputStream> barCodeOutPutStream = new ArrayList<>();

    /**pdf操作文件*/
    public Document pdfDocument = null;

    /**pdf文件輸出流*/
    public OutputStream pdfOs = null;

    /**pdf文件構造輸出流*/
    public PdfWriter pdfWriter = null;

    /**
     * 像素換算單位(1釐米約等於28.4像素)
     */
    public static float pixelUnitForCm = 28.4f;
    /**
     * 像素換算單位(1mm約等於2.84像素)
     */
    public static float pixelUnitForMm = 2.84F;

    /**
     * 設置一個標籤紙大小的紙張
     */
    public static Rectangle LABLE_PAGE = new RectangleReadOnly(5.0f * pixelUnitForCm, 3.0f * pixelUnitForCm);

    /**
     * 字體大小
     */
    public float FONT_SIZE = 5F;
    /**
     * 聲明圖片固定高度12
     */
    public int imgHeight = 12;
    /**
     * 聲明圖片固定寬度110
     */
    public int imgWidth = 110;

    public static BarCode2PdfUtil create(){
        return new BarCode2PdfUtil();
    }


    public AjaxResult creatBarCode2Pdf(List<BarCodeData> barCodeDatas,String exportPath,String exportName){
        if(CollectionUtils.isEmpty(barCodeDatas)){
            return AjaxResult.error("條形碼數據爲空,裝換失敗");
        }
        try {
            //判斷文件夾存在不,不存在則創建
            File file = new File(exportPath);
            if(!file.exists()){
                file.mkdir();
            }
            String outPath = exportPath + exportName;
            int index = 0;
            for (BarCodeData barCodeData:barCodeDatas){
                if(!ObjectUtils.isEmpty(barCodeData)){
                    //得到二維碼輸出流
                    ByteArrayOutputStream byteArrayOutputStream = creatBarCodeImg(barCodeData.getBarCode());
                    if(index == 0){
                        pdfOs = new FileOutputStream(new File(outPath));//得到pdf文件輸出流
                        createDocumentAndOpen(pdfOs);//創建pdf操作類,並打開
                        pdfDataWrite(barCodeData.getBarCode(),byteArrayOutputStream,barCodeData.getHeadShowData());//寫入數據
                    }else {
                        pdfDocument.newPage();//新建pdf頁
                        pdfDataWrite(barCodeData.getBarCode(),byteArrayOutputStream,barCodeData.getHeadShowData());//寫入數據
                    }
                    index ++;
                }
            }
            close();//關閉各種流
            return AjaxResult.success(outPath);
        }catch (Exception e){
            e.printStackTrace();
            return AjaxResult.error("轉換條形碼失敗,多次嘗試無果請聯繫管理員");
        }
    }

    public void close() throws Exception {
        //關閉條形碼輸出流
        if(!CollectionUtils.isEmpty(barCodeOutPutStream)){
            for (ByteArrayOutputStream outputStream:barCodeOutPutStream) {
                outputStream.close();
            }
        }
        //關閉pdf文件操作
        if(!ObjectUtils.isEmpty(pdfDocument)){
            pdfDocument.close();
        }
        //關閉pdf輸出流
        if(!ObjectUtils.isEmpty(pdfOs)){
            pdfOs.close();
        }
        //關閉pdf構造出的輸出流
        if(!ObjectUtils.isEmpty(pdfWriter)){
            pdfWriter.close();
        }
    }

    /**
     * 構造一個pdf操作文件
     */
    public void  createDocumentAndOpen(OutputStream pdfOs) throws Exception {
        if(pdfDocument == null){
            //設置紙張爲A4標籤紙,左右上下邊距全部爲5
            pdfDocument = new Document(LABLE_PAGE, 5, 5, 5, 5);
            // 構造好的pdf文件輸出位置
            pdfWriter = PdfWriter.getInstance(pdfDocument, pdfOs);
            //打開文件
            pdfDocument.open();
        }
    }

    /**
     * 對pdf進行數據寫入操作
     * @param barCode 條形碼序列號
     * @param barCodeImgStream 條形碼輸出流
     * @param fontData 輸出條形碼頭部顯示數據
     * @throws Exception 異常
     */
    public void pdfDataWrite(String barCode, ByteArrayOutputStream barCodeImgStream,Map<String, String> fontData) throws Exception {
        //itext在pdf中輸入中文字體(中文楷體)時:
        BaseFont bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);

        //創建表格,表格中是可以繼續嵌入表格的,指定表格爲1列
        PdfPTable table = new PdfPTable(1);//1列
        table.setHorizontalAlignment(1);//設置水平居中
        table.setWidthPercentage(98);//設置table佔頁面比98%

        //創建裝入頭部中文信息的表格
        PdfPTable tableTop = new PdfPTable(new float[]{30f,50f});
        tableTop.setHorizontalAlignment(1);//設置水平居中
        if (!ObjectUtils.isEmpty(fontData) && fontData.size() > 0) {
            for (String key : fontData.keySet()) {
                String value = fontData.get(key);
                //創建一個單元格
                PdfPCell cell = new PdfPCell();
                //插入key鍵值
                Paragraph mess = new Paragraph(key, new com.lowagie.text.Font(bfChinese,FONT_SIZE));
                mess.setAlignment(Element.ALIGN_CENTER);//設置碎屏劇中
                cell.setPhrase(mess);//將字體裝入單元格中
                setPdfPCellStyle(cell);//統一設置單元格水平垂直居中
                tableTop.addCell(cell);

                //插入key鍵value值
                cell = new PdfPCell();
                mess = new Paragraph(value, new com.lowagie.text.Font(bfChinese,FONT_SIZE));
                cell.setPhrase(mess);//將字體裝入單元格中
                setPdfPCellStyle(cell);//統一設置單元格水平垂直居中
                tableTop.addCell(cell);
            }
        }
        table.addCell(tableTop);//向全局表格中裝入頭部信息子表格

        //創建底部裝入條形碼的表格
        PdfPTable tableFoot = new PdfPTable(1);
        tableFoot.setHorizontalAlignment(1);//設置水平居中
        if(!ObjectUtils.isEmpty(barCodeImgStream)){
            //插入條形碼圖片
            byte[] bt = barCodeImgStream.toByteArray();
            com.lowagie.text.Image image = Image.getInstance(bt);
            image.scaleAbsoluteHeight(imgHeight);//設置圖片的高
            image.scaleAbsoluteWidth(imgWidth);//設置圖片的寬
            //重點,必須將設置的圖片在單元格new時放進單元格,而不是後續cell.setImg(),不然對於圖片的設置會失效
            PdfPCell cell = new PdfPCell(image);
            setPdfPCellStyle(cell);//統一設置單元格水平垂直居中
            cell.setMinimumHeight(20);//設置最小高度
            cell.setBorderWidthBottom(0);//設置單元格底部邊框爲0
            tableFoot.addCell(cell);

            //插入條形碼號
            Paragraph mess = new Paragraph(barCode, new com.lowagie.text.Font(bfChinese, 4));
            mess.setAlignment(Element.ALIGN_CENTER);
            cell = new PdfPCell();//重新new一個單元格
            cell.setBorderWidthTop(0);//設置單元格頭部邊框爲0
            cell.setPhrase(mess);//將字體裝入單元格中
            setPdfPCellStyle(cell);//統一設置單元格水平垂直居中
            tableFoot.addCell(cell);
        }
        table.addCell(tableFoot);//將底部子表格裝入大表格中
        pdfDocument.add(table);//向頁面中裝入表格
    }

    /**
     * 通用統一需要設置的單元格樣式,垂直水平居中
     *
     * @param cell1
     */
    public void setPdfPCellStyle(PdfPCell cell1) {
        //創建一個單元格
        cell1.setUseAscender(true);//開啓這個水平居中生效
        cell1.setHorizontalAlignment(Element.ALIGN_CENTER); //水平居中
        cell1.setVerticalAlignment(Element.ALIGN_MIDDLE); //垂直居中
        cell1.setMinimumHeight(10);//設置最小高度
    }

    //毫米轉像素
    public float mmTopx(float mm) {
        mm = (float) (mm * 2.84);
        return mm;
    }


    /**
     * 創建一個條形碼圖像
     * @param barCode 傳入條形碼
     * @throws Exception 異常
     */
    public ByteArrayOutputStream creatBarCodeImg(String barCode) throws Exception {
        BufferedImage image = creatBarCode().createBarcode(barCode);//得到一個圖片操作對象
        //傳入圖片對象,指定圖片格式,設置圖片像素,也就是設置寬高
        ByteArrayOutputStream barCodeOutPut = new ByteArrayOutputStream();//創建一個字節輸出流,讓條形碼輸出到字節數組流中
        barCodeOutPutStream.add(barCodeOutPut);
        ImageUtil.encodeAndWrite(image, ImageUtil.PNG,barCodeOutPut,BARCODE_DPI,BARCODE_DPI);//執行繪製圖像,會進入重寫的靜態內部內方法中
        return barCodeOutPut;
    }

    /**創建一個條形碼實例*/
    public JBarcode creatBarCode(){
        // 生成code128
        JBarcode jbc = JBarcodeFactory.getInstance().createCode128();
        jbc.setEncoder(Code128Encoder.getInstance());
        jbc.setTextPainter(CustomTextPainter.getInstance());
        return jbc;
    }

    /**
     * 靜態內部類 自定義的 TextPainter,
     * 允許定義字體,大小,文本等 參考底層實現:BaseLineTextPainter.getInstance()
     */
    protected  static class CustomTextPainter implements TextPainter {
        private static JBarcodeUtil.CustomTextPainter instance = new JBarcodeUtil.CustomTextPainter();

        public static JBarcodeUtil.CustomTextPainter getInstance() {
            return instance;
        }

        public void paintText(BufferedImage barCodeImage, String text, int width) {
            Graphics g2d = barCodeImage.getGraphics();
            g2d.fillRect(0, 0, barCodeImage.getWidth(), 0);
            g2d.fillRect(0, barCodeImage.getHeight(),barCodeImage.getWidth(),0);
        }
    }


    /**
     * 內部類,存放條形碼所敘述句
     */
    @Data
    @NoArgsConstructor
    public static class BarCodeData{
        /**條形碼編號*/
        private String barCode;
        /**頭部顯示數據*/
        private Map<String,String> headShowData;
    }

    public static void main(String[] args) {

        //模擬第一條數據
        HashMap<String, String> dataMap = new HashMap<>();
        dataMap.put("商品名稱","蘋果筆記本電腦");
        dataMap.put("聯繫人","張某某");
        dataMap.put("聯繫電話","18888889489");
        BarCodeData barCodeData = new BarCodeData();
        barCodeData.setBarCode("1231225411");
        barCodeData.setHeadShowData(dataMap);

        //模擬第二條數據
        HashMap<String, String> dataMap1 = new HashMap<>();
        dataMap1.put("商品名稱","華爲筆記本電腦");
        dataMap1.put("聯繫人","李某某");
        dataMap1.put("聯繫電話","17777779489");
        BarCodeData barCodeData1 = new BarCodeData();
        barCodeData1.setBarCode("1231225411");
        barCodeData1.setHeadShowData(dataMap1);

        ArrayList<BarCodeData> list = new ArrayList<>();
        list.add(barCodeData);
        list.add(barCodeData1);
        AjaxResult ajaxResult = BarCode2PdfUtil.create().creatBarCode2Pdf(list, "d:/file", "1.pdf");
        System.out.println(ajaxResult);
    }

}

這裏有個封裝的返回:(用過的細心地同學可以揣測出這是哪個框架)

package com.xxx.common.core.domain;

import java.util.HashMap;
import com.xxx.common.utils.StringUtils;

/**
 * 操作消息提醒
 * 
 * @author xxx
 */
public class AjaxResult extends HashMap<String, Object>
{
    private static final long serialVersionUID = 1L;

    /** 狀態碼 */
    public static final String CODE_TAG = "code";

    /** 返回內容 */
    public static final String MSG_TAG = "msg";

    /** 數據對象 */
    public static final String DATA_TAG = "data";

    /**
     * 狀態類型
     */
    public enum Type
    {
        /** 成功 */
        SUCCESS(0),
        /** 警告 */
        WARN(301),
        /** 錯誤 */
        ERROR(500);
        private final int value;

        Type(int value)
        {
            this.value = value;
        }

        public int value()
        {
            return this.value;
        }
    }

    /**
     * 初始化一個新創建的 AjaxResult 對象,使其表示一個空消息。
     */
    public AjaxResult()
    {
    }

    /**
     * 初始化一個新創建的 AjaxResult 對象
     * 
     * @param type 狀態類型
     * @param msg 返回內容
     */
    public AjaxResult(Type type, String msg)
    {
        super.put(CODE_TAG, type.value);
        super.put(MSG_TAG, msg);
    }

    /**
     * 初始化一個新創建的 AjaxResult 對象
     * 
     * @param type 狀態類型
     * @param msg 返回內容
     * @param data 數據對象
     */
    public AjaxResult(Type type, String msg, Object data)
    {
        super.put(CODE_TAG, type.value);
        super.put(MSG_TAG, msg);
        if (StringUtils.isNotNull(data))
        {
            super.put(DATA_TAG, data);
        }
    }

    /**
     * 返回成功消息
     * 
     * @return 成功消息
     */
    public static AjaxResult success()
    {
        return AjaxResult.success("操作成功");
    }

    /**
     * 返回成功數據
     * 
     * @return 成功消息
     */
    public static AjaxResult success(Object data)
    {
        return AjaxResult.success("操作成功", data);
    }

    /**
     * 返回成功消息
     * 
     * @param msg 返回內容
     * @return 成功消息
     */
    public static AjaxResult success(String msg)
    {
        return AjaxResult.success(msg, null);
    }

    /**
     * 返回成功消息
     * 
     * @param msg 返回內容
     * @param data 數據對象
     * @return 成功消息
     */
    public static AjaxResult success(String msg, Object data)
    {
        return new AjaxResult(Type.SUCCESS, msg, data);
    }

    /**
     * 返回警告消息
     * 
     * @param msg 返回內容
     * @return 警告消息
     */
    public static AjaxResult warn(String msg)
    {
        return AjaxResult.warn(msg, null);
    }

    /**
     * 返回警告消息
     * 
     * @param msg 返回內容
     * @param data 數據對象
     * @return 警告消息
     */
    public static AjaxResult warn(String msg, Object data)
    {
        return new AjaxResult(Type.WARN, msg, data);
    }

    /**
     * 返回錯誤消息
     * 
     * @return
     */
    public static AjaxResult error()
    {
        return AjaxResult.error("操作失敗");
    }

    /**
     * 返回錯誤消息
     * 
     * @param msg 返回內容
     * @return 警告消息
     */
    public static AjaxResult error(String msg)
    {
        return AjaxResult.error(msg, null);
    }

    /**
     * 返回錯誤消息
     * 
     * @param msg 返回內容
     * @param data 數據對象
     * @return 警告消息
     */
    public static AjaxResult error(String msg, Object data)
    {
        return new AjaxResult(Type.ERROR, msg, data);
    }
}

 

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