How to print Asian languages to a thermal printer from Flutter?
Asked Answered
D

6

4

I am using the Flutter package esc_pos_printer 1.5.0 to print to a thermal printer. It all works fine if I print Latin characters. I have tried using the mutilingual code page but it always fails when trying to print Thai characters. I need to be able to print in English, Thai, Burmese, Khmer and Vietnamese. None of the available code pages in this package appear to support non Latin Asian languages. This is a show stopper for me and possibly many others.

II sent an ESC command to the printer to change the code page and then printed the new code page which was in Thai and Latin characters as expected. However, my app crashes when I try to print Thai characters.

I am getting this error from the debugger:

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)

Here is my code:

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

edit: I attempted to encode the string as bytes and print like this

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

printer.sendRaw(_bytes);

but I got this

result from above commands

I used the package suggested below which works well for Thai. My ESC/POS printer supports code pages 96 and 255 for Thai. 96 gets it wrong but 255 got the job done. The bottom alignment mismatch will be because printing Thai characters require 3 passes and this string contains no bottom pass characters.

enter image description here

Destinee answered 25/12, 2019 at 5:54 Comment(0)
W
6

Unfortunetely, I think there are two issues with the esc_pos_printer package at this moment:

  • it miss many character code tables and it doesn't really allow you to pass your own using PosCodeTable constuctor since it's private, but you can set character table manually by using sendRaw

  • you can pass only characters that can be encoded using Latin1Codec or GBK codec since only these are supported, to my knowledge - Thai characters can't be encoded using these - this why the error is thrown

The problem really comes down to encoding. For Thai, printers will probably expect the data to come in format of TIS-620. Fe. ๐ is 240, ๑ is 241.

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

Remember that you do need to set the character code table. I used Thai Character Code 18 with number 26, because that is what my printer supports. To find right charset codes you may look for ESC t command in your printer manual - manufacturers usually put table there or just browse the internet.

So how do we fix it?

  1. We can fork&fix/workaround setting character code tables

  2. Use proper encoder, like GBK for Chinese, but it is not built-in since Dart support only handful amount of encodings and using Encoding.getByName("csTIS620") will get you null. You may need to do character mapping yourself - I didn't found codec for Dart that would suit your needs. There is also a package charset_converter that uses platform code to convert the charsets.

Important updates since original answer

esc_pos_printer now supports raw byte streams on Tickets by textEncoded method. This can be used with charset_converter to print right characters.

Also character code tables were expanded i.e. CP1250. This is set in PosStyles as codeTable parameter. Those are mapped to printer ids in ecs_pos_utils project with this map.

Wilow answered 30/12, 2019 at 13:8 Comment(9)
My printer has code pages for Thai at 96 and 255. Even if I could identify each character in the string it doesn't help me print above and below characters. I don't believe there is a codec in Dart for ThaiDestinee
You can try to get a codec for platform code (so Kotlin/Java for Android) and use channels to communicate with Dart or even use true libiconv with dart:ffiWilow
An answer below mentions pub.dev/packages/flutter_iconv for using dart:ffi to load iconv from C but it won't compile as it looks like the interface has changed recently and I can't figure out how to update it.Destinee
About the charset - if your use case is Android (iOS support is coming) I just published a package for that, which you can try pub.dev/packages/charset_converter - check the source code example - I used TIS620 thereWilow
How can I find out what language all those encodings represent? I can't find them on the net. I'm specifically looking for Khmer and Burmese.Destinee
Seems like with Khmer and Burmese it's even more complicated story. I would start looking from your printer specification, seems like mine doesn't support anything that would match.Wilow
Same here. I can't even find a codepage for those languages but I did see photos of esc/pos printers for sale in Cambodia with sample printouts in Khmer. Never mind, getting Thai and Vietnamese is a huge win right there.Destinee
I get a deprecation warning on first gradle build after flutter clean then ./gradlew assembleRelease appDistributionUploadRelease produces an erro "build failed" . The error relates to the java.io plugin.Destinee
Thanks! TIS-620 work for me! BTW I'm using it with axios and iconv-lite in javascriptCicily
A
3

I temporarily resolved this by converting everything to an image and printing it. I used 2 steps below:

1 - Create image from text(using canvas draw):

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

Step 2: create my custom print text function(instead using printText directly, replace it by printImage)

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

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

From my point of view, this solution has :

Advantages :

  • Any fonts (Google Fonts, etc.)

  • Any language

Disadvantages :

  • not native-feeling with your printer (the font, line spacing...)

NOTE: 

  • My printer device is the Xprinter C320H (72mm (adjustable by commands)).

  • printRow can use the same flow, just draw rows  data (the code is very long and ugly, so I will not post it here @@).

Ascomycete answered 25/1, 2022 at 7:47 Comment(2)
After wasting 2 days, this seems to be the best solid solution at the moment.Gatt
Printing as image is so much slower, were you able to speed it up?Langley
B
1

What is the vendor and model of your printer?
And are the parameters for the Thai code page specification correct?
It seems that there are multiple patterns of 20-26 instead of 255 in EPSON materials.

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

And whether the Thai language is supported is divided according to the model number of the printer and the more detailed destination.
Support Information for Each Model: Code Pages

Then try the print request as a binary data array, not an encoded string.


In Addition:
For example, if you have a library like iconv - npm or iconv-lite - npm that works in a browser, you will be able to perform various encoding/decoding. Please look for it.

If such a library can convert to a byte array, can it be sent with printer.sendRaw() as described in the question article?


There was such a library huaji249/flutter_iconv, but the creator seems to support only their own use(GBK to UTF-8).
Try creating your own by referring to this.

There are other articles on how to use JavaScript libraries.
Flutter and Openlayers - including js libraries in flutter


Thai characters will be in this range in UTF-8.
U+0E00...U+0E7F:Thai

And here is the typical MBCS Thai code page.
ISO/IEC 8859-11 - Wikipedia

With these numbers of characters, it seems easy to create a dedicated function to convert one character at a time using a conversion table.
It may be better to create a dedicated function with just the functionality you need, rather than looking for or creating a generic conversion library.

The conversion destination should be a Thai character table supported by your printer.


Further additions:

I found such an article. To print Thai characters, print them in three lines.
This article may be an alternative, depending on the features your printer can support.
Print Thai language

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.

And, there is another article like this.
Thai characters printing #51
Thai character problem of the printer model TM-T88IV #701
Epson TM-T82II Thai language support #409


Do you need to make it a PDF?
For example, if you convert from canvas directly to an image and print it, isn't it necessary to take time-consuming file creation and reading?

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

Bakke answered 27/12, 2019 at 15:42 Comment(7)
For the languages not supported, how could I convert the strings to binary? Thai can have 3 lines to print per character.Destinee
The printer is generic but has 2 Thai code pages, 96 and 255. It also has a Vietnamese code page 27 but no Khmer or Burmese. The problem appears to be that the Flutter package does not support those code pagesDestinee
This must be do-able in Dart. The demand for this functionality must be immense.Destinee
This application is Flutter on android, not Flutter web.Destinee
It seems like flutter_iconv just called libiconv.so. How about changing the parameters to convert from UTF-8 to Thai? If that is the case, you may be able to use the flutter android app. If that doesn't work, you'll need to find a library compatible with iconv created with native Dart, or create your own.Bakke
The project huaji249/flutter_iconv has 7 compile errors. I can't find a native Dart version of iconv.Destinee
This is the analysis page for flutter_iconv: pub.dev/packages/flutter_iconv#-analysis-tab-Destinee
S
0

u can use this

you can check this

this repo use 3 laiblary

secreenshot to convert widget to image and image library to convert it to uint8 and pos_print to print it ass u love to show

u can check it from here

Selfwill answered 23/3, 2022 at 0:9 Comment(0)
C
0

You have to find the character table for your printer and write the corresponding commands to let the printer know that you are printing multi byte characters such as Arabic characters. In my case, I was using sunmi printer and the only thing that worked for me was finding its character table and I wrote the commands and it worked very well. Here's a picture of what they said in the documentation. a picture of what they said in the documentation

And this is what I did and it worked perfectly

const utf8Encoder = Utf8Encoder();
final encodedStr = utf8Encoder.convert(invoice.description);
bytes += generator.textEncoded(Uint8List.fromList([
  ...[0x1C, 0x26, 0x1C, 0x43, 0xFF],
  ...encodedStr
]));
Collarbone answered 1/6, 2022 at 20:11 Comment(3)
not working for meRhodesia
What printer do you use? @SaeedGhasemiCollarbone
It's an inbuilt printer(Citaq H10 POS)Rhodesia
S
0

If you can print image from lib esc_pos_printer or another lib.

final ByteData data = await rootBundle.load('assets/logo.png');

final Uint8List bytes = data.buffer.asUint8List(); // use capture image from lib screenshort

final Image image = decodeImage(bytes);

generator.image(image);

you can use lib screenshot for capture image from widget and gen to Uint8List for use

screenshotController
  .captureFromWidget(Container(
      padding: const EdgeInsets.all(10.0),
      child: Text("Hello สวัสดี")))
  .then((Uint8List capturedImage) {
// Handle Uint8List capture image
});
Stump answered 29/4 at 12:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.