如何在 Swiss QR invoice 中合理放置填写框
结论
不要自行压缩或重新设计填写框。Swiss QR invoice(通常称为 Swiss QR-bill)的支付部分有明确的尺寸和排版要求,需要同时参考两份文档:
- Swiss Implementation Guidelines QR-bill:规定 QR 数据结构、字段和业务规则。
- Style Guide QR-bill:规定 payment part、receipt、填写框、字体、间距和分隔线等视觉样式。
右侧空间不足,通常不是规范遗漏,而是页面显示了不该出现的字段,或者没有按照 Style Guide 对地址、空金额和付款人信息进行条件排版。还要特别确认系统中的 final creditor 是否指规范中的 Ultimate Creditor。该数据组虽然存在于数据模型,但在长期发布的实施规则中一直是”保留供将来使用”的字段。能否使用,应以项目当前采用的官方规范版本为准,不能因为数据模型中有这个字段就直接显示。
先确认页面的固定尺寸
完整付款单区域位于 A4 页面底部,高度为 105 mm,总宽度为 210 mm:
- Receipt:
62 mm × 105 mm - Payment part:
148 mm × 105 mm
Payment part 不是可以自由流动排版的普通文档区域。QR code、Account / Payable to、Reference、Additional information、Payable by、Currency 和 Amount 都有固定位置或规定的可用范围。
如果使用 HTML、PDF 表格自动布局,或者根据内容动态增加高度,很容易造成字段下移、填写框缩小或内容越界。更稳妥的做法是使用以毫米为单位的绝对坐标,并严格限制每个文本块的最大行数。
不要同时显示所有可能字段
排版时应根据数据是否存在,选择对应的显示形式,不要为所有字段预留空间。
金额
金额已知时,直接输出数值:
Currency Amount
CHF 1 250.00
只有金额未知时,才显示手写填写区域。填写框的尺寸和位置必须符合 Style Guide,不能为了给其他文字腾出空间而随意缩小。
付款人
如果有 Ultimate Debtor 地址,显示 Payable by 和对应地址。
如果没有该地址,则显示 Payable by (name/address) 以及规定大小的空白填写框。这两种形式不能同时出现。
Ultimate Creditor
需要确认系统中的 final creditor address 是否对应规范里的 Ultimate Creditor。如果当前实施指南仍将它列为保留字段,就不应:
- 写入 QR payload;
- 在 payment part 上打印;
- 为其额外预留多行空间。
这通常是出现”至少 13 行文本”问题的原因之一。
地址行数必须受控
QR-bill 的地址应采用规范支持的结构化地址或组合地址,并放在固定区域内。不要把数据库中的任意多行邮寄地址原样复制到付款单。
典型的结构化地址如下:
Company AG
Example Street 10
8000 Zurich
CH
生成器应遵守以下约束:
- 根据地址类型组合街道、门牌号、邮编和城市。
- 删除空字段造成的空行。
- 使用规范允许的字号和行距。
- 按规范处理过长内容,包括截断或缩放。
- 不要通过无限换行来扩大文本块。
字号也不能缩小到 Style Guide 允许的范围之外。测试数据能够放下,不代表扫描、打印和人工填写时仍然合规。
Java 实现方式
实际项目中,建议使用专门的 Swiss QR-bill 库生成支付部分,而不是只使用普通 QR code 库。普通二维码库只能编码二维码图形,无法处理版面、Swiss cross、地址规则、参考号校验和空白填写框。
Java 生态中可以考虑 qrbill-generator。下面是一个典型用法。具体包名和方法签名可能随库版本变化,应以所选版本的文档为准:
import net.codecrete.qrbill.generator.Address;
import net.codecrete.qrbill.generator.Bill;
import net.codecrete.qrbill.generator.OutputFormat;
import net.codecrete.qrbill.generator.QRBill;
import java.math.BigDecimal;
public class InvoicePaymentPart {
public static byte[] createQrBill() {
Bill bill = new Bill();
bill.setAccount("CH4431999123000889012");
Address creditor = Address.createStructuredAddress(
"Example Company AG",
"Example Street",
"10",
"8000",
"Zurich",
"CH"
);
bill.setCreditor(creditor);
bill.setCurrency("CHF");
bill.setAmount(new BigDecimal("1250.00"));
bill.setReference("210000000003139471430009017");
bill.setUnstructuredMessage("Invoice 2026-001");
return QRBill.generate(bill, OutputFormat.PDF);
}
}
如果金额需要由付款人填写,就不要设置金额:
bill.setCurrency("CHF");
// 不设置 amount,生成器应输出相应的手写金额区域。
如果付款人地址也需要手写,则不要设置 debtor:
// 不设置 debtor,生成器应输出 Payable by (name/address) 填写框。
如果所用版本不支持 PDF,可以先生成 SVG,再将其嵌入 PDF:
byte[] svg = QRBill.generate(bill, OutputFormat.SVG);
接口名称需要根据实际依赖版本核对,尤其是 setAmount、地址工厂方法和输出格式。不要仅根据示例固定依赖版本。
推荐的实现流程
- 确定业务当前采用的 Swiss QR-bill 规范版本。
- 下载同一版本的 Implementation Guidelines 和 Style Guide。
- 根据业务数据判断实际存在哪些字段。
- 排除当前规范禁止使用或标记为保留的字段,特别是
Ultimate Creditor。 - 使用现成库生成 payment part 或完整 QR-bill。
- 按原始物理尺寸将生成结果嵌入发票,禁止自动缩放。
- 使用官方或银行提供的验证工具检查 QR payload。
- 分别测试已知金额、空金额、已知付款人和手写付款人等组合。
- 打印后使用不同银行应用扫描,并检查裁切、缩放和字体效果。
常见问题
只生成二维码是否足够?
不够。Swiss QR-bill 不只规定二维码内容,也规定周围支付部分的视觉布局。二维码正确,不代表整张付款单合规。
可以调整填写框大小吗?
原则上不应自行调整。填写区域的尺寸、边框形式和位置应以当前 Style Guide 为准。如果空间不足,应先检查字段使用方式和地址换行,不要先压缩填写框。
可以把付款单缩放后放进发票吗?
不建议。PDF 查看器或打印驱动中的”适合页面”功能可能改变物理尺寸。生成 PDF 时应使用毫米设置页面和坐标,打印测试时则应选择 100% 或”实际大小”。
为什么规范正文对布局描述不多?
数据规则和视觉规则通常写在不同文档中。Implementation Guidelines 主要说明”编码什么”,Style Guide 说明”如何呈现”。实现时需要配套使用这两份文档。
备注:内容仅供参考。
