最近項目需要用到條形碼,對於資產進行管理追蹤,所以相應的就需要用到生成條形碼的工具,在網上查找了相關資料之後,目前有幾類產品,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);
}
}