thermal_unicode_print 0.0.1 copy "thermal_unicode_print: ^0.0.1" to clipboard
thermal_unicode_print: ^0.0.1 copied to clipboard

Prints any Unicode script (Bangla, Arabic, Devanagari, ...) on ESC/POS thermal printers by rasterizing text with dart:ui instead of screenshotting a Flutter widget tree. Renders one bounded line/row a [...]

Print Bangla — or any Unicode script an ESC/POS printer can't render natively (Devanagari, Arabic, Thai, ...) — on cheap thermal receipt printers, one bounded line at a time.

Package Version LICENSE Last Commit

thermal_unicode_print #

Most ESC/POS thermal printers only understand a handful of built-in Latin codepages. Anything outside that — Bangla, Hindi, Arabic, and most other non-Latin scripts — has to travel to the printer as a bitmap, because the printer itself cannot render the glyphs.

The common workaround is to build the whole receipt as a real on-screen Flutter widget (often inside an invisible AlertDialog), wrap it in a RepaintBoundary, and screenshot the whole thing. It works, but memory scales with the entire receipt — a large order means one very tall widget, which means toImage() has to rasterize one huge canvas, which can crash the app on lower-RAM devices.

thermal_unicode_print takes a different approach: you call one render per receipt line (a text block, or a tabular row), the same way you'd call Generator.text() / Generator.row() from esc_pos_utils_plus for Latin text. Output size per call stays constant no matter how long the whole receipt is, because you never rasterize more than one line's worth of content at a time.

Features #

  • Print any Unicode script an ESC/POS printer supports as a font — not limited to Bangla.
  • Renders one bounded line/row at a time, so memory use doesn't grow with receipt length.
  • Tabular rows with proportional column widths (ThermalCell(flex: ...), mirroring Expanded inside a Row).
  • Full-width divider rule.
  • Every render method also has a PNG variant, for an on-screen preview of exactly what will print — no physical printer required during development.
  • Works with esc_pos_utils_plus's Generator, so it drops into an existing ESC/POS receipt pipeline.

Installation #

Add the package to your pubspec.yaml:

flutter pub add thermal_unicode_print esc_pos_utils_plus

Then import it:

import 'package:thermal_unicode_print/thermal_unicode_print.dart';

Bring your own font #

The renderer is script-agnostic — it just needs a TextStyle whose fontFamily actually covers the script you're printing (e.g. Noto Sans Bengali for Bangla). Bundle the font file in your own app instead of fetching it at runtime (e.g. via google_fonts's network download): a POS device at the counter has to be able to print with no internet connection.

  1. Download a font that covers your script — Noto Sans covers nearly every script and is free to redistribute.

  2. Put it under assets/fonts/ in your app and declare it in your app's pubspec.yaml:

    flutter:
      fonts:
        - family: NotoSansBengali
          fonts:
            - asset: assets/fonts/NotoSansBengali-Variable.ttf
    
  3. Reference that fontFamily in the TextStyle you pass to textLine/row.

See example/pubspec.yaml and example/assets/fonts/ for a complete, working setup.

One requirement: an Overlay in your widget tree #

Complex-script text is rendered through a real, briefly-mounted Text widget rather than a fully detached canvas (see Why not a plain Canvas? below) — this needs an Overlay somewhere in the widget tree, which MaterialApp / CupertinoApp / WidgetsApp already provide. Call render methods after runApp(), not from main() before it.

Usage #

import 'package:esc_pos_utils_plus/esc_pos_utils_plus.dart';
import 'package:flutter/material.dart';
import 'package:thermal_unicode_print/thermal_unicode_print.dart';

Future<List<int>> buildReceipt() async {
  final profile = await CapabilityProfile.load();
  final generator = Generator(PaperSize.mm58, profile);
  const renderer = ThermalUnicodeRenderer(dotsWidth: 384); // 58mm @ 203dpi

  final bytes = <int>[];
  bytes.addAll(await renderer.textLine(
    generator,
    'ফ্রেশো শপ',
    align: TextAlign.center,
    style: const TextStyle(
      fontFamily: 'NotoSansBengali',
      fontSize: 32,
      fontWeight: FontWeight.bold,
    ),
  ));
  bytes.addAll(await renderer.divider(generator));
  bytes.addAll(await renderer.row(generator, const [
    ThermalCell('চাল (মিনিকেট)', flex: 5),
    ThermalCell('২', flex: 2, align: TextAlign.center),
    ThermalCell('১৩০.০০', flex: 3, align: TextAlign.right),
  ]));
  bytes.addAll(generator.feed(1));
  bytes.addAll(generator.cut());
  return bytes;
}

Then send bytes to the printer with whichever Bluetooth/USB printing package you already use, e.g. print_bluetooth_thermal:

await PrintBluetoothThermal.writeBytes(await buildReceipt());

For a full multi-item receipt, build a List of steps (text lines, rows, dividers) and loop over it, appending each step's bytes — see example/lib/main.dart for a complete worked example, including a live on-screen preview via the ...Png() variants so you can see what will print without a physical printer.

API #

  • ThermalUnicodeRenderer(dotsWidth: ...)dotsWidth is the printer's printable width in dots (384 for 58mm paper, 576 for 80mm, both at the typical 203dpi).
  • textLine(generator, text, {style, align, padding}) — one (optionally word-wrapped) text block.
  • row(generator, cells, {style, columnGap}) — one tabular row; ThermalCell(text, {flex, align, bold}) mirrors Expanded(flex: ...) inside a Row.
  • divider(generator, {thickness, verticalPadding}) — a full-width horizontal rule.
  • Each of the above has an ...Image() variant (raw image package img.Image) and a ...Png() variant (PNG bytes) for previewing or further composition without a printer.

Why not a plain Canvas and TextPainter? #

An earlier version of this package painted text with a detached Canvas/TextPainter/PictureRecorder, with no widget tree involved at all. That turned out to be unreliable on some Android devices' Impeller renderer configurations: the text would lay out correctly (non-zero height) but paint completely blank — no exception, no error, just zero ink — even though the identical font/text in a normal Text widget rendered fine.

To work around it, each render briefly mounts a real Text/Row widget off-screen in the nearest Overlay, captures it via RenderRepaintBoundary, then removes it — invisible to the user, but a real, composited frame, which is what actually renders the text correctly.

Demo #

For a complete example app, see the example directory.

Help #

Found a bug? Report it here. Have a feature request? Request it here.

Changelog #

See CHANGELOG.md for detailed version history.

Contributions #

Contributions are welcome! If you encounter any issues or have suggestions for improvements, please feel free to open an issue or submit a pull request.

License #

Released under the MIT License.

0
likes
150
points
89
downloads

Documentation

API reference

Publisher

verified publisherpritom.me

Weekly Downloads

Prints any Unicode script (Bangla, Arabic, Devanagari, ...) on ESC/POS thermal printers by rasterizing text with dart:ui instead of screenshotting a Flutter widget tree. Renders one bounded line/row at a time, so output size never grows with receipt length.

Repository (GitHub)
View/report issues

Topics

#printing #thermal-printer #bangla #unicode #escpos

License

MIT (license)

Dependencies

esc_pos_utils_plus, flutter, image

More

Packages that depend on thermal_unicode_print