portakal_flutter 0.1.7
portakal_flutter: ^0.1.7 copied to clipboard
Universal printer language SDK — TSC, ZPL, EPL, ESC/POS, CPCL, DPL, IPL, SBPL, Star PRNT. Text, images, printer-native commands, and Flutter preview widgets.
🍊 portakal_flutter #
Universal printer language SDK for Flutter & Dart — TSC, ZPL, EPL, ESC/POS, CPCL, DPL, IPL, SBPL, Star PRNT and more.
Text, images, printer-native barcode/QR commands, and Flutter preview widgets.
Note: Barcode/QR generation is NOT in this package. Use
etiketfor barcode & QR code generation (SVG/PNG). Portakal sends printer-native barcode/QR commands (the printer's built-in encoder) OR accepts pre-rendered images for pixel-perfect output.
Features #
- 🖨️ 9 printer languages — TSC, ZPL, EPL, ESC/POS, CPCL, DPL, IPL, SBPL, Star PRNT
- 🔄 Cross-compilation — convert between any supported languages
- 🏷️ Fluent label builder — design labels with a clean, chainable API
- 📝 HTML-like markup —
<label><text>Hello</text></label>DSL - 🔍 Parsers — parse raw printer commands back to structured data
- 👁️ SVG preview — render labels as SVG strings for visual inspection
- 📱 Flutter preview widget — preview labels in-app before sending to printer
- ✅ Validation — check command ordering, parameter ranges, and structure
- 🖼️ Image processing — RGBA to monochrome with 4 dithering algorithms
- 📇 17 printer profiles — Epson, Zebra, TSC, Star, Bixolon, Citizen, SATO, Honeywell
- 💯 Works across Flutter targets — mobile, desktop, and web
Installation #
dependencies:
portakal_flutter: ^0.1.6
Example #
dart run example/main.dart
flutter run -t example/flutter_preview.dart
Quick Start #
Build & compile a label #
import 'package:portakal_flutter/portakal_flutter.dart';
// Build a label with the fluent API
final myLabel = label(LabelConfig(width: 40, height: 30, unit: Unit.mm))
.text('Hello World', TextOptions(x: 10, y: 10, font: '2', size: 2))
.box(BoxOptions(x: 5, y: 5, width: 310, height: 230, thickness: 2))
.circle(CircleOptions(x: 250, y: 150, diameter: 60));
// Compile to TSC
final tscCode = tsc.compile(myLabel);
// SIZE 40 mm,30 mm
// GAP 3 mm,0 mm
// CLS
// TEXT 10,10,"2",0,2,2,"Hello World"
// BOX 5,5,315,235,2
// CIRCLE 250,150,60,1
// PRINT 1
// Same label → ZPL
final zplCode = zpl.compile(myLabel);
// ^XA
// ^FO10,10^A0N,60,60^FDHello World^FS
// ^FO5,5^GB310,230,2^FS
// ^FO250,150^GC60,1^FS
// ^XZ
HTML-like markup #
import 'package:portakal_flutter/portakal_flutter.dart';
final code = tsc.compile(markup('''
<label width="100mm" height="150mm" dpi="203">
<text x="10" y="10" size="2" bold>FROM: Warehouse A</text>
<text x="10" y="40" size="3" bold>TO: John Doe</text>
<text x="10" y="80">123 Main St, New York, NY 10001</text>
<line x1="5" y1="110" x2="780" y2="110" thickness="2" />
<box x="5" y="5" width="780" height="1170" border="3" />
</label>
'''));
Cross-compilation #
import 'package:portakal_flutter/portakal_flutter.dart';
// Convert TSC code to ZPL
final result = convert(tscCode, 'tsc', 'zpl');
print(result.output); // ZPL string
// Convert ZPL to ESC/POS (binary)
final receipt = convert(zplCode, 'zpl', 'escpos');
print(receipt.output); // Uint8List
Parse printer commands #
import 'package:portakal_flutter/portakal_flutter.dart';
// Parse TSC
final tscResult = tsc.parse('SIZE 40 mm,30 mm\nCLS\nTEXT 10,10,"2",0,1,1,"Hello"\nPRINT 1');
print(tscResult.commands.length); // 4
print(tscResult.widthDots); // 320
print(tscResult.elements.length); // 1 (TextElement)
// Parse ZPL
final zplResult = zpl.parse('^XA^FO10,10^A0N,30,30^FDHello^FS^XZ');
print(zplResult.commands.length); // 6
SVG preview #
import 'package:portakal_flutter/portakal_flutter.dart';
final svg = tsc.preview(
label(LabelConfig(width: 40, height: 30))
.text('Preview Test', TextOptions(x: 10, y: 10, size: 2))
.box(BoxOptions(x: 5, y: 5, width: 310, height: 230, thickness: 2)),
);
// Returns SVG string — render in a WebView or save to file
Flutter widget preview #
import 'package:flutter/material.dart';
import 'package:portakal_flutter/portakal_flutter.dart';
class LabelPreviewCard extends StatelessWidget {
const LabelPreviewCard({super.key});
@override
Widget build(BuildContext context) {
final myLabel = label(LabelConfig(width: 40, height: 30))
.text('Preview before print', TextOptions(x: 12, y: 20, size: 2))
.box(BoxOptions(x: 8, y: 8, width: 300, height: 200, thickness: 2));
return Card(
child: Padding(
padding: const EdgeInsets.all(12),
child: LabelPreview(label: myLabel),
),
);
}
}
Validate commands #
import 'package:portakal_flutter/portakal_flutter.dart';
// Validate TSC
final r = tsc.validate('SIZE 40 mm,30 mm\nCLS\nTEXT 10,10,"2",0,1,1,"Hi"\nPRINT 1');
print(r.valid); // true
print(r.errors); // 0
// Catch errors
final bad = tsc.validate('TEXT 10,10,"2",0,1,1,"No CLS"\nPRINT 1');
print(bad.valid); // false
print(bad.issues); // [CLS must appear before label elements]
Image processing #
import 'dart:typed_data';
import 'package:portakal_flutter/portakal_flutter.dart';
// Convert RGBA pixels to monochrome bitmap
final rgba = Uint8List(100 * 100 * 4); // 100x100 RGBA image
final bitmap = imageToMonochrome(rgba, 100, 100);
// bitmap.data — 1-bit packed bitmap
// bitmap.bytesPerRow — bytes per scanline
// Use with label builder
final myLabel = label(LabelConfig(width: 40, height: 30))
.image(bitmap, ImageOptions(x: 10, y: 10));
Printer profiles #
import 'package:portakal_flutter/portakal_flutter.dart';
// Get a specific profile
final profile = getProfile('zebra-zd420');
print(profile?.dpi); // 203
print(profile?.language); // zpl
print(profile?.dotsPerLine); // 864
// Find by USB vendor ID
final epsonPrinters = findByVendorId(0x04B8);
// Find by language
final zplPrinters = findByLanguage('zpl');
// Use with label builder — auto-sets DPI
final myLabel = label(LabelConfig(
width: 40, height: 30,
printer: 'tsc-te310', // Uses 300 DPI from profile
));
Receipt formatting (ESC/POS) #
import 'package:portakal_flutter/portakal_flutter.dart';
// Format a receipt line
final line = formatPair('Hamburger', '\$12.99', 32);
// "Hamburger $12.99"
// Word wrap
final lines = wordWrap('This is a long text that needs wrapping', 20);
// ["This is a long text", "that needs wrapping"]
// Table
final rows = formatTable(
[Column(width: 20), Column(width: 8, align: 'right'), Column(width: 4, align: 'right')],
[['Hamburger', '\$12.99', 'x2'], ['Fries', '\$4.99', 'x1']],
32,
);
Supported Languages #
| Language | Compile | Parse | Preview | Validate |
|---|---|---|---|---|
| TSC/TSPL2 | ✅ | ✅ | ✅ | ✅ Full |
| ZPL II | ✅ | ✅ | ✅ | ✅ Full |
| EPL2 | ✅ | ✅ | ✅ | ℹ️ Basic |
| ESC/POS | ✅ | ✅ | ✅ | ℹ️ Basic |
| CPCL | ✅ | ✅ | ✅ | ℹ️ Basic |
| DPL | ✅ | ✅ | ✅ | ℹ️ Basic |
| IPL | ✅ | ✅ | ✅ | ℹ️ Basic |
| SBPL | ✅ | ✅ | ✅ | ℹ️ Basic |
| Star PRNT | ✅ | ✅ | ✅ | ℹ️ Basic |
Label Elements #
| Element | Description | TSC | ZPL | EPL | ESC/POS | CPCL | DPL | IPL | SBPL | Star PRNT |
|---|---|---|---|---|---|---|---|---|---|---|
text |
Text with font, size, rotation | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
image |
Monochrome bitmap | ✅ | ✅ | ✅ | ✅ | ✅ | — | — | ✅ | ✅ |
box |
Rectangle with optional radius | ✅ | ✅ | ✅ | — | ✅ | ✅ | ✅ | ✅ | — |
line |
Line between two points | ✅ | ✅ | ⚠️ | — | ✅ | ⚠️ | ⚠️ | ⚠️ | — |
circle |
Circle with thickness | ✅ | ✅ | — | — | — | — | — | — | — |
ellipse |
Ellipse | ✅ | — | — | — | — | — | — | — | — |
reverse |
Invert colors in region | ✅ | — | — | — | — | — | — | — | — |
erase |
Clear region to white | ✅ | ✅ | — | — | — | — | — | — | — |
raw |
Raw command passthrough | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
⚠️ indicates a best-effort approximation (for example, non-diagonal-only line behavior).
Dithering Algorithms #
Four algorithms for converting images to 1-bit monochrome:
| Algorithm | Best For |
|---|---|
threshold |
Simple graphics, logos, barcodes |
floydSteinberg |
Photos, gradients (best quality) |
atkinson |
Retro aesthetic, lighter output |
ordered |
Fast, predictable patterns |
API Reference #
Language Modules #
Each language module (tsc, zpl, epl, escpos, cpcl, dpl, ipl, sbpl, starprnt) exports:
String compile(LabelBuilder builder) // Label → printer commands
Result parse(String code) // Printer commands → structured data
String preview(LabelBuilder builder) // Label → SVG string
Result validate(String code) // Check command validity
Core Functions #
LabelBuilder label(LabelConfig config) // Create label builder
LabelBuilder markup(String source) // Parse HTML-like markup
ConvertResult convert(String code, String from, String to, {...}) // Cross-compile
String renderPreview(ResolvedLabel label) // SVG preview
Result validate(String code, String language) // Validate commands
Development #
# Run tests
dart test
# Run a specific test
dart test test/tsc_test.dart
# Static analysis
dart analyze
Credits #
Dart/Flutter port of portakal by productdevbook.
License #
MIT