Open ttyniwa opened 2 years ago
this is work fine for me
// 返回POS协议的打印字节
Future<List<int>> getPosPrintBytes(TicketPrintTask data) async {
List<int> bytes = [];
// Xprinter XP-N160I
final profile = await CapabilityProfile.load();
// PaperSize.mm80 or PaperSize.mm58
final generator = Generator(PaperSize.mm80, profile);
int maxCharsPerLine = 48;
// 设置行距
generator.spaceBetweenRows = 5;
// 设置居中对齐
generator.setStyles(const PosStyles(align: PosAlign.center));
// 添加小票标题
bytes.addAll(generator.emptyLines(2));
bytes.addAll(generator.text("【${data.name}】",
containsChinese: true,
styles: const PosStyles(
align: PosAlign.center,
bold: true,
width: PosTextSize.size2,
height: PosTextSize.size2,
)));
bytes.addAll(generator.emptyLines(1));
// 打印主要内容
bytes += getTileBytes(generator, data.body, maxCharsPerLine);
// 打印商品
bytes += getItemBytes(generator, data.items, maxCharsPerLine);
// 打印汇总信息
bytes += getTileBytes(generator, data.summary, maxCharsPerLine);
// 打印底脚信息
bytes += getFooterBytes(generator, data.footer);
// 推纸
bytes += generator.feed(5);
// 切纸
bytes += generator.cut();
// bytes += generator.image(thumbnail);
return bytes;
}
/// 打印小票主体信息
List<int> getTileBytes(
Generator generator, List<PrintField> body, int maxCharsPerLine) {
if (body.isEmpty) return [];
List<int> bytes = [];
bytes.addAll(generator.emptyLines(1));
// 打印水平线
bytes.addAll(generator.hr());
for (var it in body) {
it.label = it.label.trim();
it.value = it.value.trim();
var txt =
it.label + _getSpace(maxCharsPerLine, it.label + it.value) + it.value;
bytes += generator.text(txt,
containsChinese: true, styles: const PosStyles(align: PosAlign.left));
}
return bytes;
}
/// 打印商品信息
List<int> getItemBytes(
Generator generator, List<TicketDetailsItem> items, int maxCharsPerLine) {
List<int> bytes = [];
for (var it in items) {
if (it.labels.isEmpty || it.data.isEmpty) continue;
bytes.addAll(generator.emptyLines(1));
if (it.title.isNotEmpty) {
bytes.addAll(generator.emptyLines(1));
bytes.addAll(generator.text(it.title, containsChinese: true));
}
// 打印水平线
bytes.addAll(generator.hr());
var i = 0;
for (var data in it.data) {
var j = 0;
List<PosColumn> arr = [];
// 打页列头
if (i == 0) {
for (var label in it.labels) {
arr.add(PosColumn(
text: label.name.isEmpty ? "-" : label.name,
width: j == 0 ? 12 - (it.labels.length - 1) * 2 : 2,
containsChinese: true,
styles: PosStyles(
align: j == it.labels.length - 1
? PosAlign.right
: PosAlign.left)));
j++;
}
bytes += _printRow(generator, arr, maxCharsPerLine);
bytes.addAll(generator.hr());
arr = [];
j = 0;
}
for (var label in it.labels) {
arr.add(PosColumn(
text:
(data[label.key] ?? "-").isEmpty ? "-" : data[label.key] ?? "-",
width: j == 0 ? 12 - (it.labels.length - 1) * 2 : 2,
containsChinese: true,
styles: PosStyles(
align: j == it.labels.length - 1
? PosAlign.right
: PosAlign.left)));
j++;
}
bytes += _printRow(generator, arr, maxCharsPerLine);
i++;
}
}
return bytes;
}
/// 打印底部信息
List<int> getFooterBytes(Generator generator, PrintFooter footer) {
List<int> bytes = [];
const boldStyle = PosStyles(bold: true);
const centerStyle = PosStyles(align: PosAlign.center);
bytes.addAll(generator.emptyLines(1));
bytes.addAll(generator.hr());
if (footer.text.isNotEmpty) {
bytes.addAll(
generator.text(footer.text, containsChinese: true, styles: boldStyle));
bytes.add(generator.spaceBetweenRows * 2);
}
if (footer.text.isNotEmpty) {
bytes.addAll(generator.text(footer.instructions,
containsChinese: true, styles: boldStyle));
bytes.add(generator.spaceBetweenRows * 2);
}
if (footer.phone.isNotEmpty) {
bytes.addAll(generator.text(footer.phone,
containsChinese: true, styles: centerStyle));
bytes.add(generator.spaceBetweenRows * 1);
}
if (footer.suggest.isNotEmpty) {
bytes.addAll(generator.hr());
bytes.addAll(generator.text(footer.suggest,
containsChinese: true, styles: centerStyle));
}
return bytes;
}
/// 打印行信息,因为直接打印行会导致中英文重叠
List<int> _printRow(Generator geneartor, List<PosColumn> arr, int max) {
int unitChars = max * 2 ~/ 12; // 每个单元格应显示的长度
int firstColChars = max - (arr.length - 1) * unitChars; // 第一行应显示的长度
int firstMaxChars = firstColChars ~/ 2;
// 第一行没有显示完的字符
String firstCutChars = arr[0].text.length > firstMaxChars
? arr[0].text.substring(firstMaxChars)
: "";
String firstColTxt = firstCutChars.isEmpty
? arr[0].text
: arr[0].text.substring(0, firstMaxChars);
String firstLine = firstColTxt + _getSpace(firstColChars, firstColTxt);
for (int i = 1; i < arr.length; i++) {
if (i == arr.length - 1) {
// 右对齐
firstLine += _getSpace(unitChars, arr[i].text) + arr[i].text;
} else {
// 左对齐
firstLine += arr[i].text + _getSpace(unitChars, arr[i].text);
}
}
var bytes = geneartor.text(firstLine, containsChinese: true);
while (firstCutChars.isNotEmpty) {
bytes.add(geneartor.spaceBetweenRows);
var isOver = firstCutChars.length <= firstMaxChars;
var firstColTxt =
isOver ? firstCutChars : firstCutChars.substring(firstMaxChars);
bytes.addAll(geneartor.text(firstColTxt + _getSpace(max, firstColTxt),
containsChinese: true));
firstCutChars = isOver ? "" : firstCutChars.substring(firstMaxChars);
}
return bytes;
}
int _getStringLen(String word) {
RegExp chineseRegexp = RegExp("[\u4e00-\u9fa5]");
int chLen = chineseRegexp.allMatches(word).length;
var t = (word.length + chLen);
if (word.contains("¥") || word.contains("(") || word.contains(")")) {
t += 1;
}
return t;
}
// 计算同一行用省略号代替的长度
String _getSpace(int max, String word) {
var t = max - _getStringLen(word);
List<String> bytes = [];
for (var i = 0; i < t; i++) {
bytes.add(" ");
}
return bytes.join("");
}
Fix CJK(Chinese, Japanese and Korean) String overlap when using Generator.row() method.
before:
after: