最近项目需要用到条形码,对于资产进行管理追踪,所以相应的就需要用到生成条形码的工具,在网上查找了相关资料之后,目前有几类产品,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 CustomTextPainter instance = new CustomTextPainter();
public static 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);
}
}