thermal_unicode_print 0.0.1
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.
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: ...), mirroringExpandedinside aRow). - 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.
-
Download a font that covers your script — Noto Sans covers nearly every script and is free to redistribute.
-
Put it under
assets/fonts/in your app and declare it in your app'spubspec.yaml:flutter: fonts: - family: NotoSansBengali fonts: - asset: assets/fonts/NotoSansBengali-Variable.ttf -
Reference that
fontFamilyin theTextStyleyou pass totextLine/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: ...)—dotsWidthis 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})mirrorsExpanded(flex: ...)inside aRow.divider(generator, {thickness, verticalPadding})— a full-width horizontal rule.- Each of the above has an
...Image()variant (rawimagepackageimg.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.