You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java发票打印:如何添加店铺标题并调整字体大小

你的现有方案(直接发送纯文本流给打印机)没办法实现字体大小调整,因为这种方式本质是让打印机用默认配置的字体打印纯文本,没有提供在代码中控制字体样式、大小的接口。要实现放大店铺标题的需求,你需要更换方案,下面给你两种适合的实现方式,根据你的打印机类型和需求选择:

方案1:利用ESC/POS指令(适合针式打印机,比如你的EPSON LQ-310)

很多针式打印机支持ESC/POS控制指令,通过在文本中插入特定的字节指令,可以切换字体大小、加粗等格式。比如:

  • ESC ! 0x10 可设置双倍宽度和高度的字体(不同打印机指令可能略有差异,建议查对应型号的官方手册)
  • ESC ! 0x00 恢复默认字体

修改你的代码,在标题前后加入这些指令即可:

import java.awt.print.PrinterJob;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.print.Doc;
import javax.print.DocFlavor;
import javax.print.DocPrintJob;
import javax.print.PrintException;
import javax.print.PrintService;
import javax.print.SimpleDoc;
import javax.print.event.PrintJobAdapter;
import javax.print.event.PrintJobEvent;

public class GenerateInvoice {
    // ESC/POS指令定义
    private static final byte ESC = 0x1B;
    private static final byte EXCLAMATION_MARK = 0x21;
    private static final byte DOUBLE_SIZE = 0x10; // 双倍宽高,具体值参考打印机手册
    private static final byte DEFAULT_SIZE = 0x00;

    public static void printInvoice(String invoice) throws IOException {
        try {
            PrintService mPrinter = null;
            PrintService[] printServices = PrinterJob.lookupPrintServices();
            for (PrintService printService : printServices) {
                if (printService.getName().equals("EPSON LQ-310")) {
                    mPrinter = printService;
                    break;
                }
            }
            if (mPrinter == null) {
                System.out.println("目标打印机未找到");
                return;
            }

            // 构建带格式的发票内容
            StringBuilder invoiceContent = new StringBuilder();
            // 插入放大字体指令
            invoiceContent.append(new String(new byte[]{ESC, EXCLAMATION_MARK, DOUBLE_SIZE}));
            // 添加店铺标题
            invoiceContent.append(String.format("%30s\n\n", " MY SHOP TITLE "));
            // 恢复默认字体指令
            invoiceContent.append(new String(new byte[]{ESC, EXCLAMATION_MARK, DEFAULT_SIZE}));
            // 添加原有发票内容
            invoiceContent.append(invoice);
            // 添加换页符
            invoiceContent.append("\f");

            InputStream is = new ByteArrayInputStream(invoiceContent.toString().getBytes());
            DocFlavor flavor = DocFlavor.INPUT_STREAM.AUTOSENSE ;
            DocPrintJob job = mPrinter.createPrintJob();
            Doc doc= new SimpleDoc(is, flavor, null);
            PrintJobWatcher pjDone = new PrintJobWatcher(job);
            job.print(doc, null);
            pjDone.waitForDone();
            is.close();
        } catch (PrintException e) {
            e.printStackTrace();
        }
    }

    static class PrintJobWatcher {
        boolean done = false;

        PrintJobWatcher(DocPrintJob job) {
            job.addPrintJobListener(new PrintJobAdapter() {
                public void printJobCanceled(PrintJobEvent pje) { allDone(); }
                public void printJobCompleted(PrintJobEvent pje) { allDone(); }
                public void printJobFailed(PrintJobEvent pje) { allDone(); }
                public void printJobNoMoreEvents(PrintJobEvent pje) { allDone(); }

                void allDone() {
                    synchronized (PrintJobWatcher.this) {
                        done = true;
                        PrintJobWatcher.this.notify();
                    }
                }
            });
        }

        public synchronized void waitForDone() {
            try {
                while (!done) {
                    wait();
                }
            } catch (InterruptedException e) {}
        }
    }
}

注意:不同品牌型号的打印机ESC/POS指令可能有细微差别,比如字体大小的参数、初始化指令等,建议查阅EPSON LQ-310的官方手册确认具体指令。

方案2:使用Java AWT Printing API(更灵活,支持任意字体格式)

如果需要更复杂的布局(比如不同字体、对齐方式、甚至插入图片),推荐用Java的AWT打印API,它允许你直接在打印页面上绘制内容,完全控制字体大小和样式:

import java.awt.*;
import java.awt.print.*;

public class FormattedInvoicePrinter implements Printable {
    private String invoiceBody;
    private String shopTitle;

    public FormattedInvoicePrinter(String shopTitle, String invoiceBody) {
        this.shopTitle = shopTitle;
        this.invoiceBody = invoiceBody;
    }

    @Override
    public int print(Graphics g, PageFormat pf, int pageIndex) throws PrinterException {
        if (pageIndex > 0) {
            return NO_SUCH_PAGE;
        }

        Graphics2D g2d = (Graphics2D) g;
        g2d.translate(pf.getImageableX(), pf.getImageableY());

        // 设置店铺标题的大字体(加粗+24号)
        Font titleFont = new Font("Arial", Font.BOLD, 24);
        g2d.setFont(titleFont);
        // 计算标题居中的X坐标
        FontMetrics titleMetrics = g2d.getFontMetrics(titleFont);
        int titleX = (int) (pf.getImageableWidth() - titleMetrics.stringWidth(shopTitle)) / 2;
        g2d.drawString(shopTitle, titleX, 50); // 50是标题的Y坐标,可按需调整

        // 设置正文的默认字体
        Font contentFont = new Font("Arial", Font.PLAIN, 12);
        g2d.setFont(contentFont);
        FontMetrics contentMetrics = g2d.getFontMetrics(contentFont);
        int y = 100; // 正文起始Y坐标
        // 拆分正文为行,逐行绘制
        String[] lines = invoiceBody.split("\n");
        for (String line : lines) {
            g2d.drawString(line, 20, y);
            y += contentMetrics.getHeight();
        }

        return PAGE_EXISTS;
    }

    public static void printFormattedInvoice(String shopTitle, String invoiceBody) {
        PrinterJob job = PrinterJob.getPrinterJob();
        // 查找目标打印机
        PrintService targetPrinter = null;
        PrintService[] printServices = job.lookupPrintServices();
        for (PrintService ps : printServices) {
            if (ps.getName().equals("EPSON LQ-310")) {
                targetPrinter = ps;
                break;
            }
        }
        if (targetPrinter == null) {
            System.out.println("目标打印机未找到");
            return;
        }
        try {
            job.setPrintService(targetPrinter);
            job.setPrintable(new FormattedInvoicePrinter(shopTitle, invoiceBody));
            if (job.printDialog()) {
                job.print();
            }
        } catch (PrinterException e) {
            e.printStackTrace();
        }
    }

    // 调用示例
    public static void main(String[] args) {
        String invoiceBody = String.format("%5s %10s %15s %20s\n", "Description","Unit","Quantity","Net Amount")
                + "Item1      $10        2          $20\n"
                + "Item2      $15        1          $15";
        printFormattedInvoice("MY SHOP TITLE", invoiceBody);
    }
}

这种方案的优势是格式完全可控,不管什么打印机都能保证布局一致;缺点是需要手动处理换行、对齐等细节。

方案选择建议

  • 如果你只是需要简单放大标题,且打印机支持ESC/POS,方案1改动小、实现快;
  • 如果需要更复杂的格式控制,或者打印机不支持ESC/POS,方案2是更通用的选择。

内容的提问来源于stack exchange,提问作者kushan_d

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:43:48