Java如何实现批量打印条形码

 更新时间:2025年05月15日 09:01:52   作者:Katie。  
这篇文章主要为大家详细介绍了如何基于 ZXing 库生成条形码位图,并利用 Java 打印服务(PrinterJob)将它们以网格方式批量打印到 A4 标签纸或标签纸格式,需要的小伙伴可以参考一下

一、项目背景详细介绍

在仓储管理、商品追溯、物流配送等场景中,条形码(Barcode)作为物品唯一标识被广泛使用。为提高效率,经常需要将一批数据转为条形码并打印在标签纸或 A4 页面上,满足打包扫描、入库出库等作业流程。

传统做法可能依赖专有打标签软件,灵活性差且难以集成到定制化系统。使用 Java 开发一套 批量生成并打印条形码 的工具,可以:

灵活集成:嵌入现有仓储或 ERP 系统,直接从数据库或 CSV 导入数据;

定制标签:支持调整条形码大小、分辨率、文字位置;

自动化批量:一键批量生成、分页排版、提交给打印机;

零第三方依赖:仅需开源库 ZXing 和 Java 自带打印 API。

本项目将演示如何基于 ZXing 库生成条形码位图,利用 Java 打印服务(PrinterJob)将它们以网格方式批量打印到 A4 标签纸或标签纸格式(如 3×10 布局)。

二、项目需求详细介绍

2.1 功能需求

1.批量数据导入

支持从文本文件(CSV)、数据库或内存列表读取多条待编码内容;

2.条形码生成

  • 使用 ZXing 生成 Code128 或 EAN-13 条形码;
  • 支持指定宽度、高度、边距及是否显示文本;

3.分页排版

  • 在 A4 纸或标签纸上按指定行列(如 2 列×5 行)自动排版;
  • 支持页眉页脚或批次号打印;

4.打印预览(可选)

弹出打印对话框预览分页及打印机设置;

5.提交打印

通过 PrinterJob 接口,支持选择打印机及打印范围;

6.错误处理与日志

捕获生成失败、打印失败,记录日志并跳过继续;

2.2 非功能需求

易用性:一行命令或简单 GUI 即可完成;

配置化:页面尺寸、行列数、条码样式通过配置文件管理;

跨平台:仅依赖 Java SE 与 ZXing,可在 Windows、Linux、macOS 上运行;

可扩展:后续可支持二维码(QR Code)等多种格式;

三、相关技术详细介绍

1.ZXing (com.google.zxing)

  • 主流开源条码/二维码生成与解析库;
  • 支持多种码制:Code128, EAN13, QR_CODE 等;

2.Java2D (java.awt.Graphics2D)

在 BufferedImage 上绘制条码位图;

3.PrinterJob (java.awt.print)

  • 标准打印 API:PrinterJob、PageFormat、Printable;
  • 支持页面设置与打印对话框;

4.配置管理

使用 java.util.Properties 或 Spring @ConfigurationProperties 加载标签布局;

5.日志框架

SLF4J + Logback 记录运行状态;

四、实现思路详细介绍

1.加载配置与数据

  • 从 config.properties 读取:页面宽度、高度、行数、列数、条形码尺寸、边距等;
  • 从 CSV 或数据库加载待打印的编码字符串列表;

2.生成条形码图像

  • 对每条内容调用 ZXing 的 MultiFormatWriter.encode(),生成 BitMatrix;
  • 将 BitMatrix 转换为 BufferedImage,并在底部添加文本标签(可选);

3.分页排版

实现 Printable 接口:

  • 在 print(Graphics g, PageFormat pf, int pageIndex) 中,根据 pageIndex 计算当前页的数据子列表;
  • 对每行每列绘制对应条码图像,使用 g.drawImage();
  • 如有文字批次号或条码文本,使用 Graphics2D.drawString();
  • 返回 PAGE_EXISTS 或 NO_SUCH_PAGE;

4.打印提交

PrinterJob job = PrinterJob.getPrinterJob(); job.setPrintable(printable, pageFormat); if(job.printDialog()) job.print();

5.日志与异常

条码生成或打印异常时,捕获并记录,跳过当前码或重试;

五、完整实现代码

import com.google.zxing.*;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.oned.Code128Writer;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.print.*;
import java.io.*;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
public class BatchBarcodePrinter implements Printable {
    private static final Logger logger = LoggerFactory.getLogger(BatchBarcodePrinter.class);
 
    private List<String> codes;         // 待打印内容
    private Properties config;          // 布局及条码配置
    private List<BufferedImage> images; // 生成的条码图像列表
    private int cols, rows;             // 每页列数和行数
    private int imgWidth, imgHeight;    // 条码图像尺寸
    private int marginX, marginY;       // 页面边距
    private int gapX, gapY;             // 图像间隔
 
    public BatchBarcodePrinter(List<String> codes, Properties cfg) throws Exception {
        this.codes = codes;
        this.config = cfg;
        loadConfig();
        generateImages();
    }
 
    // 读取配置
    private void loadConfig() {
        cols       = Integer.parseInt(config.getProperty("page.cols", "2"));
        rows       = Integer.parseInt(config.getProperty("page.rows", "5"));
        imgWidth   = Integer.parseInt(config.getProperty("barcode.width", "200"));
        imgHeight  = Integer.parseInt(config.getProperty("barcode.height", "80"));
        marginX    = Integer.parseInt(config.getProperty("page.marginX", "50"));
        marginY    = Integer.parseInt(config.getProperty("page.marginY", "50"));
        gapX       = Integer.parseInt(config.getProperty("page.gapX", "20"));
        gapY       = Integer.parseInt(config.getProperty("page.gapY", "30"));
    }
 
    // 生成所有条码图像
    private void generateImages() {
        images = new ArrayList<>();
        Code128Writer writer = new Code128Writer();
        for (String code : codes) {
            try {
                BitMatrix bm = writer.encode(code, BarcodeFormat.CODE_128, imgWidth, imgHeight);
                BufferedImage img = new BufferedImage(imgWidth, imgHeight + 20, BufferedImage.TYPE_INT_RGB);
                Graphics2D g = img.createGraphics();
                g.setColor(Color.WHITE);
                g.fillRect(0, 0, img.getWidth(), img.getHeight());
                // 绘制条码
                for (int x = 0; x < imgWidth; x++) {
                    for (int y = 0; y < imgHeight; y++) {
                        g.setColor(bm.get(x, y) ? Color.BLACK : Color.WHITE);
                        g.fillRect(x, y, 1, 1);
                    }
                }
                // 绘制文本
                g.setColor(Color.BLACK);
                g.setFont(new Font("Monospaced", Font.PLAIN, 14));
                FontMetrics fm = g.getFontMetrics();
                int tx = (imgWidth - fm.stringWidth(code)) / 2;
                g.drawString(code, tx, imgHeight + fm.getAscent());
                g.dispose();
                images.add(img);
            } catch (Exception e) {
                logger.error("生成条码失败: {}", code, e);
            }
        }
    }
 
    @Override
    public int print(Graphics graphics, PageFormat pf, int pageIndex) {
        int perPage = cols * rows;
        int start = pageIndex * perPage;
        if (start >= images.size()) return NO_SUCH_PAGE;
        Graphics2D g = (Graphics2D) graphics;
        g.translate(pf.getImageableX(), pf.getImageableY());
        int x0 = marginX, y0 = marginY;
        // 绘制本页条码
        for (int i = 0; i < perPage; i++) {
            int idx = start + i;
            if (idx >= images.size()) break;
            int row = i / cols, col = i % cols;
            int x = x0 + col * (imgWidth + gapX);
            int y = y0 + row * (imgHeight + gapY + 20);
            g.drawImage(images.get(idx), x, y, null);
        }
        return PAGE_EXISTS;
    }
 
    // 启动打印
    public void printAll() throws PrinterException {
        PrinterJob job = PrinterJob.getPrinterJob();
        job.setPrintable(this);
        if (job.printDialog()) {
            job.print();
        }
    }
 
    public static void main(String[] args) throws Exception {
        // 1. 加载待打印数据
        List<String> codes = new ArrayList<>();
        try (BufferedReader br = new BufferedReader(new FileReader("codes.csv"))) {
            String line;
            while ((line = br.readLine()) != null) {
                if (!line.trim().isEmpty()) codes.add(line.trim());
            }
        }
        // 2. 加载配置
        Properties cfg = new Properties();
        try (InputStream in = new FileInputStream("config.properties")) {
            cfg.load(in);
        }
        // 3. 批量打印
        BatchBarcodePrinter printer = new BatchBarcodePrinter(codes, cfg);
        printer.printAll();
    }
}

六、代码详细解读

1.配置加载

从 config.properties 中读取:page.cols, page.rows(每页行列数)、barcode.width, barcode.height(条码图像尺寸)、页边距与间隔等,便于灵活调整布局。

2.条码生成

使用 ZXing 的 Code128Writer 生成 BitMatrix,逐像素渲染到 BufferedImage,并在下方绘制文本。

3.实现 Printable

print(...) 根据 pageIndex 计算本页起始索引,并按行列遍历绘制对应条码图像,返回 PAGE_EXISTS 或 NO_SUCH_PAGE 控制分页。

4.打印提交

PrinterJob 弹出打印对话框,用户可选择打印机与份数,调用 print() 发起。

5.主方法流程

  • 从 codes.csv 读取待打印条码列表;
  • 从 config.properties 加载布局与尺寸;
  • 构造 BatchBarcodePrinter 并调用 printAll()。

七、项目详细总结

本项目展示了如何用 Java + ZXing + PrinterJob 实现 批量打印条形码:

数据灵活:支持从文件或数据库导入任意数量的编码字符串;

配置驱动:行列数、图像大小、页边距等通过外部配置调整;

圖形生成:ZXing 可靠生成高质量 Code128 条码,并可扩展到 QR Code;

分页打印:标准 Java 打印 API 自动分页,兼容任意打印机;

零商业依赖:仅用开源组件即可完成企业级标签打印需求。

适合物流、仓储、制造业等需要批量生产标签的场景,也可作为 Java 图形与打印 API 教学案例。

八、项目常见问题及解答

Q1:条码图像模糊或尺寸不符?

A:确认 imgWidth/imgHeight 与实际打印机 DPI 配合,必要时增加分辨率或调整页面缩放。

Q2:打印出现空白页?

A:检查 print() 返回值逻辑,确保当 start >= images.size() 时返回 NO_SUCH_PAGE,否则会多打印空页。

Q3:条码下方文字不居中?

A:文本绘制使用 FontMetrics 计算字符串宽度,确保 x = (imgWidth - textWidth)/2。

Q4:需要打印二维码?

A:将 Code128Writer 换为 QRCodeWriter,并设置 BitMatrix 尺寸,文本可放到旁边或不显示。

九、扩展方向与性能优化

大批量流式生成:对超万条数据,分批生成图像并实时打印,避免一次性占用大量内存;

自定义标签纸尺寸:支持常见标签纸(如 4×8 标签)模板配置,精确匹配打印机纸张;

并发打印:多线程调用不同 PrinterJob 实例同时发送到多台打印机;

网络打印集成:支持通过 IPP 协议或打印服务器推送打印作业;

RTF/Word 导出:在 Word 文档或 RTF 中嵌入条码图像,扩展报表输出;

GUI 预览与编辑:用 Swing 或 JavaFX 提供可视化布局工具,让用户拖拽调整标签位置。

到此这篇关于Java如何实现批量打印条形码的文章就介绍到这了,更多相关Java打印条形码内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 在Eclipse中运行Solr 基础知识

    在Eclipse中运行Solr 基础知识

    Solr我还是个菜鸟,写这一些文章只是记录一下最近一段时间学习Solr的心得,望各位同仁不要见笑,还希望多多指点
    2012-11-11
  • spring jpa 审计功能自定义填充字段方式

    spring jpa 审计功能自定义填充字段方式

    这篇文章主要介绍了spring jpa审计功能自定义填充字段方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • Java中注解@JsonFormat与@DateTimeFormat的使用

    Java中注解@JsonFormat与@DateTimeFormat的使用

    从数据库获取时间传到前端进行展示的时候,我们有时候可能无法得到一个满意的时间格式的时间日期,本文主要介绍了Java中注解@JsonFormat与@DateTimeFormat的使用,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2023-08-08
  • Spring Boot WebSocket 两种集成方式详解

    Spring Boot WebSocket 两种集成方式详解

    WebSocket 是实现服务器主动推送、实时通信的利器,常见于聊天室、消息通知、实时监控大屏等场景,本文详细介绍了SpringBoot集成WebSocket的两种方式,感兴趣的朋友跟随小编一起看看吧
    2026-05-05
  • Springboot2.3.x整合Canal的示例代码

    Springboot2.3.x整合Canal的示例代码

    canal是阿里开源mysql binlog 数据组件,canal-server 才是canal的核心我们前边所讲的canal的功能,实际上讲述的就是canal-server的功能,本文给大家介绍Springboot2.3.x整合Canal的示例代码,需要的朋友可以参考下
    2022-02-02
  • Java中常用的设计模式之责任链模式详解

    Java中常用的设计模式之责任链模式详解

    这篇文章主要为大家详细介绍了Java中常用的设计模式之责任链模式,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-02-02
  • Java 深入探讨设计模式之原型模式篇

    Java 深入探讨设计模式之原型模式篇

    设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性
    2021-10-10
  • Java中id,pid格式数据转树和森林结构工具类实现

    Java中id,pid格式数据转树和森林结构工具类实现

    本文主要介绍了Java中id,pid格式数据转树和森林结构工具类实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-05-05
  • 深入理解Java高级特性——注解

    深入理解Java高级特性——注解

    这篇文章主要介绍了Java高级特性——注解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • Java catch与throw同时使用的操作

    Java catch与throw同时使用的操作

    这篇文章主要介绍了Java catch与throw同时使用的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-02-02

最新评论