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
相关产品推荐
相关产品推荐

