JavaFX应用热敏打印机发票打印字符串格式化对齐问题
JavaFX热敏打印机发票列对齐问题
我在JavaFX应用中实现热敏打印机发票打印功能时,遇到了字符串格式化问题,发票内容无法按预期对齐,尤其是**Qty(数量)和Price(价格)**列。
功能逻辑:生成发票内容并打印至热敏打印机,若未找到打印机则使用系统默认打印介质。我尝试通过添加额外空格调整格式,但没能实现列的正确对齐。以下是我的实现代码及当前输出效果,求解决。
实现代码
public static String padRight(String s, int n) { return String.format("%-" + n + "s", s); } public static String padRightInt(int s, int n) { return String.format("%-" + n + "d", s); } public static void printIvoice(Node node){ Printer printer = Printer.getDefaultPrinter(); PrinterJob printerJob = PrinterJob.createPrinterJob(printer); double scale = 0.791; node.getTransforms().add(new Scale(scale, scale)); boolean success = printerJob.printPage(node); if (success) { printerJob.endJob(); } } public static Node getPrintableText(String[] products){ Label text = new Label(); text.setFont(Font.font("Monospaced Regular", FontWeight.LIGHT, 11)); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(" Company Name\n"); stringBuilder.append("---------------------------------------------\n"); stringBuilder.append(String.format("%-40s %5s %10s\n", "Item", "Qty", "Price")); stringBuilder.append(String.format( "%-40s %5s %10s\n", "----", "---", "-----")); for(String product: products){ if(product.trim().length() > 25){ String firstLine = product.trim().substring(0, 25); stringBuilder.append(firstLine).append(" \n"); String lastLine = product.trim().substring(25); int spaces = 50 - lastLine.length(); stringBuilder.append( String.format(padRight(lastLine, spaces), lastLine) + String.format(padRightInt(1, 10), 1) +" "+ String.format(padRightInt(1000, 10), 1000)).append("\n"); }else{ String temp = product.trim(); int spaces = 50 - temp.length(); stringBuilder.append( String.format(padRight(temp, spaces), temp) + String.format(padRightInt(1, 10), 1) +" "+ String.format(padRightInt(1000, 10), 1000)).append("\n"); } } stringBuilder.append("_____________________________________________\n"); stringBuilder.append("Net Amount 5000\n"); stringBuilder.append("_____________________________________________\n"); stringBuilder.append("Thank you so much!\n"); text.setText(stringBuilder.toString()); return text; }
主方法
public static void main(String[] args) { String[] productName = { "Android", "Mac OS", "Windows 10 Operating System", "Linux Operating System", "Windows Server OS" }; Node node = getPrintableText(productName); printIvoice(node); }
当前输出效果

问题分析与解决办法
核心问题
- 格式拼接逻辑混乱:手动计算空格的方式错误,且混用
String.format和自定义padRight方法,导致列宽计算偏差。 - 多行列对齐失效:长产品名称换行后,第二行未与其他行的Qty、Price列保持对齐,未预留固定列宽位置。
- 组件渲染偏差:
Label对多行等宽文本的排版不够精准,容易出现间距异常。
修改后的代码
import javafx.scene.Node; import javafx.scene.text.Font; import javafx.scene.text.FontWeight; import javafx.scene.text.Text; import javafx.print.Printer; import javafx.print.PrinterJob; import javafx.scene.transform.Scale; public class InvoicePrinter { // 统一列宽常量,方便维护 private static final int ITEM_COL_WIDTH = 30; private static final int QTY_COL_WIDTH = 8; private static final int PRICE_COL_WIDTH = 12; public static void printInvoice(Node node) { Printer printer = Printer.getDefaultPrinter(); PrinterJob printerJob = PrinterJob.createPrinterJob(printer); if (printerJob == null) { System.err.println("无法创建打印任务"); return; } double scale = 0.791; node.getTransforms().add(new Scale(scale, scale)); boolean success = printerJob.printPage(node); if (success) { printerJob.endJob(); } else { System.err.println("打印失败"); } } public static Node getPrintableText(String[] products) { Text text = new Text(); text.setFont(Font.font("Monospaced", FontWeight.LIGHT, 11)); StringBuilder stringBuilder = new StringBuilder(); // 表头部分 stringBuilder.append(String.format("%" + 45 + "s%n", "Company Name")); stringBuilder.append("---------------------------------------------%n"); stringBuilder.append(String.format("%-" + ITEM_COL_WIDTH + "s %-" + QTY_COL_WIDTH + "s %-" + PRICE_COL_WIDTH + "s%n", "Item", "Qty", "Price")); stringBuilder.append(String.format("%-" + ITEM_COL_WIDTH + "s %-" + QTY_COL_WIDTH + "s %-" + PRICE_COL_WIDTH + "s%n", "----", "---", "-----")); // 产品列表部分 for (String product : products) { String trimmedProduct = product.trim(); if (trimmedProduct.length() > ITEM_COL_WIDTH) { // 长文本拆分,第一行仅显示Item内容 stringBuilder.append(String.format("%-" + ITEM_COL_WIDTH + "s%n", trimmedProduct.substring(0, ITEM_COL_WIDTH))); // 第二行显示剩余内容,对齐Qty和Price列 String remaining = trimmedProduct.substring(ITEM_COL_WIDTH); stringBuilder.append(String.format("%-" + ITEM_COL_WIDTH + "s %-" + QTY_COL_WIDTH + "d %-" + PRICE_COL_WIDTH + "d%n", remaining, 1, 1000)); } else { // 短文本直接按列对齐 stringBuilder.append(String.format("%-" + ITEM_COL_WIDTH + "s %-" + QTY_COL_WIDTH + "d %-" + PRICE_COL_WIDTH + "d%n", trimmedProduct, 1, 1000)); } } // 底部合计 stringBuilder.append("_____________________________________________%n"); stringBuilder.append(String.format("%-" + (ITEM_COL_WIDTH + QTY_COL_WIDTH) + "s %" + PRICE_COL_WIDTH + "d%n", "Net Amount", 5000)); stringBuilder.append("_____________________________________________%n"); stringBuilder.append(String.format("%" + 45 + "s%n", "Thank you so much!")); text.setText(stringBuilder.toString()); return text; } public static void main(String[] args) { String[] productName = { "Android", "Mac OS", "Windows 10 Operating System", "Linux Operating System", "Windows Server OS" }; Node node = getPrintableText(productName); printInvoice(node); } }
修改要点
- 定义统一列宽常量:将Item、Qty、Price的列宽固化,避免手动计算错误。
- 简化格式化逻辑:全程使用
String.format语法,确保每列宽度严格一致。 - 优化长文本换行:长产品名称拆分后,第二行仍占用Item列宽度,保证Qty和Price列对齐。
- 替换组件:用
Text替代Label,提升多行等宽文本的渲染精度。 - 增加异常处理:打印任务创建失败时给出提示,增强代码健壮性。
内容的提问来源于stack exchange,提问作者Qaisar Shabbir
相关产品推荐
相关产品推荐

