Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix CJK String overlap when using Generator.row() method. #62

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

ttyniwa
Copy link

@ttyniwa ttyniwa commented Oct 7, 2021

Fix CJK(Chinese, Japanese and Korean) String overlap when using Generator.row() method.

before:
IMG_3418

after:
IMG_3416

@ixre
Copy link

ixre commented Feb 6, 2023

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("");
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants