如何从 Flutter 将亚洲语言打印到热敏打印机?

How to print Asian languages to a thermal printer from Flutter?

我正在使用 Flutteresc_pos_printer 1.5.0 打印到热敏打印机。如果我打印拉丁字符,一切正常。我曾尝试使用多语言代码页,但在尝试打印泰语字符时总是失败。我需要能够用英语、泰语、缅甸语、高棉语和越南语打印。此包中的 None 可用代码页似乎支持非拉丁亚洲语言。这对我和其他许多人来说都是一个表演障碍。

我向打印机发送了一个 ESC 命令以更改代码页,然后按预期打印了泰文和拉丁字符的新代码页。但是,当我尝试打印泰语字符时,我的应用程序崩溃了。

我从调试器中收到此错误:

E/flutter (29402): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: Invalid argument (string): Contains invalid characters.: "ยินดีต้อนรับ"
E/flutter (29402): #0      _UnicodeSubsetEncoder.convert  (dart:convert/ascii.dart:88:9)
E/flutter (29402): #1      Latin1Codec.encode  (dart:convert/latin1.dart:42:46)

这是我的代码:

void _printReceipt(BuildContext context) {

  String ip = '192.168.1.100'; 

  Printer.connect(ip, port: 9100).then((printer) {

  printer.sendRaw([27, 116, 255]);

  printer.printCodeTable();

  printer.println('Welcome');

  printer.println('ยินดีต้อนรับ');

  printer.cut();
  printer.disconnect();
}
);
}

编辑:我尝试将字符串编码为字节并像这样打印

_bytes = utf8.encode("ยินดีต้อนรับ");

printer.sendRaw(_bytes);

但我明白了

我使用了适合泰语的 package suggested below。我的 ESC/POS 打印机支持泰语代码页 96 和 255。 96 人做错了,但 255 人完成了工作。底部对齐不匹配是因为打印泰语字符需要 3 遍并且此字符串不包含底部字符。

您的打印机供应商和型号是什么?
泰语代码页规范的参数是否正确?
EPSON的资料里好像有20-26的多种图案,而不是255的。

ESC t Select character code table

20 Page 20 Thai Character Code 42
21 Page 21 Thai Character Code 11
22 Page 22 Thai Character Code 13
23 Page 23 Thai Character Code 14
24 Page 24 Thai Character Code 16
25 Page 25 Thai Character Code 17
26 Page 26 Thai Character Code 18

International Character Sets

而是否支持泰语是根据打印机的型号和更详细的目的地来划分的。
Support Information for Each Model: Code Pages

然后尝试将打印请求作为二进制数据数组,而不是编码字符串。


另外:
例如,如果你有一个像 iconv - npm or iconv-lite - npm 这样可以在浏览器中运行的库,你将能够执行各种 encoding/decoding。请寻找它。

如果这样的库可以转换为字节数组,是否可以像问题文章中描述的那样用printer.sendRaw()发送?


原来有这样的库huaji249/flutter_iconv,不过作者好像只支持自己用(GBK转UTF-8)
参考这个尝试创建你自己的。

还有其他文章介绍如何使用 JavaScript 库。


泰语字符在 UTF-8 中将在此范围内。
U+0E00...U+0E7F:Thai

这里是典型的 MBCS 泰语代码页。
ISO/IEC 8859-11 - Wikipedia

有了这些字符数,似乎很容易创建一个专用函数来使用转换一次转换一个字符 table。
最好创建一个只包含您需要的功能的专用函数,而不是寻找或创建一个通用的转换库。

转换目标应该是您的打印机支持的泰文字符 table。


进一步补充:

我找到了这样一篇文章。要打印泰文字符,请将它们打印成三行。
根据您的打印机可以支持的功能,本文可能是替代方法。

Thai language is called a three-pass language. Which means print the top line, middle line and bottom line separately.
For example รื่ has top and middle characters, you must print them on each lines, from your picture these are printed at bottom, hence as top line.
For another example ดั้, has double top characters, you need to look up the character in the code page.
There are others bottom characters, these will be printed at top hence as bottom line.
Do some testing, you will get it.

而且,还有一篇这样的文章
Thai characters printing #51
Thai character problem of the printer model TM-T88IV #701
Epson TM-T82II Thai language support #409


您需要将其设为 PDF 吗?
比如直接从canvas转成图片打印出来,是不是需要费时费力的创建文件和读取文件?

How to save a Flutter canvas as a bitmap image?

  1. Create a PictureRecorder.
  2. Create a Canvas with your PictureRecorder and draw stuff.
  3. Call endRecording() on the PictureRecorder to get a Picture.
  4. Call toImage() on the Picture.
  5. Call toByteData() on the Image. Oops, this isn't implemented yet. I filed an issue.

Flutter dart:ui Picture toImage method

esc_pos_printer package documentation

Print image:

import 'dart:io';
import 'package:image/image.dart';

const String filename = './logo.png';
final Image image = decodeImage(File(filename).readAsBytesSync());
// Using (ESC *) command
printer.printImage(image);
// Using an alternative obsolette (GS v 0) command
printer.printImageRaster(image);

Flutter image Decoder decodeImage abstract method

不幸的是,我认为目前 esc_pos_printer 包有两个问题:

  • 它错过了许多字符代码 tables 并且它实际上不允许您使用 PosCodeTable 构造函数传递自己的代码,因为它是私有的,但您可以设置字符 table 手动使用 sendRaw

  • 您只能传递可以使用 Latin1Codec or GBK codec 编码的字符,因为据我所知,仅支持这些字符 - 无法使用这些编码泰文字符 - 这就是错误的原因抛出

问题真正归结为编码。对于泰语,打印机可能希望数据以 TIS-620 格式出现。铁。 ๐是240,๑是241.

// Manually set PosCodeTable - 26 here is the Thai Character Code 18
printer.sendRaw([
  27,
  116,
  26
]);
 
printer.sendRaw([240, 241]); // Should print ๐ and ๑

请记住,您确实需要设置字符代码 table。我使用泰国字符代码 18 和数字 26,因为这是我的打印机支持的。要找到正确的字符集代码,您可以在打印机手册中查找 ESC t 命令 - 制造商通常将 table 放在那里或只是 browse the internet.

那么我们该如何解决呢?

  1. 我们可以fork&fix/workaround设置字符代码tables

  2. 使用合适的编码器,比如中文的 GBK,但它不是内置的,因为 Dart 只支持少数编码,使用 Encoding.getByName("csTIS620") 会得到 null。您可能需要自己进行字符映射——我没有找到适合您需要的 Dart 编解码器。还有一个包 charset_converter 使用平台代码来转换字符集。

自原始答案以来的重要更新

esc_pos_printer 现在通过 textEncoded 方法支持票证上的原始字节流。这可以与 charset_converter 一起使用以打印正确的字符。

字符代码 table 也被扩展,即 CP1250。这在 PosStyles 中设置为 codeTable 参数。这些被映射到 ecs_pos_utils 项目中的打印机 ID,带有此 map.

我通过将所有内容转换为图像并打印来暂时解决了这个问题。我使用了以下 2 个步骤:

1 - 从文本创建图像(使用 canvas 绘制):

    Future<Uint8List> _generateImageFromString(
    String text,
    ui.TextAlign align,
  ) async {
    ui.PictureRecorder recorder = new ui.PictureRecorder();
    Canvas canvas = Canvas(
        recorder,
        Rect.fromCenter(
          center: Offset(0, 0),
          width: 550,
          height: 400, // cheated value, will will clip it later...
        ));
    TextSpan span = TextSpan(
      style: const TextStyle(
        color: Colors.black,
        fontSize: 20,
        fontWeight: ui.FontWeight.bold,
      ),
      text: text,
    );
    TextPainter tp = TextPainter(
        text: span,
        maxLines: 3,
        textAlign: align,
        textDirection: TextDirection.ltr);
    tp.layout(minWidth: 550, maxWidth: 550);
    tp.paint(canvas, const Offset(0.0, 0.0));
    var picture = recorder.endRecording();
    final pngBytes = await picture.toImage(
      tp.size.width.toInt(),
      tp.size.height.toInt() - 2, // decrease padding
    );
    final byteData = await pngBytes.toByteData(format: ui.ImageByteFormat.png);
    return byteData!.buffer.asUint8List();
  }

第 2 步:创建我的自定义打印文本函数(而不是直接使用 printText,将其替换为 printImage)

final imageBytes = await _generateImageFromString(
  textToPrint,
  TextAlign.center,
);

final posImage.Image? image = posImage.decodeImage(imageBytes);
printer.image(image!);

在我看来,这个解决方案有:

优点:

  • 任意字体(Google字体等)

  • 任何语言

缺点:

  • native-feeling 不适用于您的打印机(字体、行距...)

注意:

  • 我的打印机设备是Xprinter C320H(72mm(可通过指令调整))。

  • printRow可以使用相同的流程,只是绘制行数据(代码很长很丑,所以我不会post这里@@)。

你可以用这个

你可以看看这个

这个 repo 使用 3 个 laiblary

secreenshot 将小部件转换为图像 和图像库将其转换为 uint8 和 pos_print 打印它你喜欢展示

你可以从 here

查看

你得为你的打印机找到字符table,并编写相应的命令,让打印机知道你正在打印阿拉伯字符等多字节字符。就我而言,我使用的是 sunmi 打印机,唯一对我有用的是找到它的字符 table 并且我编写了命令并且它运行良好。这是他们在文档中所说内容的图片。 a picture of what they said in the documentation

这就是我所做的,而且效果很好

const utf8Encoder = Utf8Encoder();
final encodedStr = utf8Encoder.convert(invoice.description);
bytes += generator.textEncoded(Uint8List.fromList([
  ...[0x1C, 0x26, 0x1C, 0x43, 0xFF],
  ...encodedStr
]));