Skip to content
2 changes: 1 addition & 1 deletion example/example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Future<void> main() async {
width: 3,
styles: PosStyles(align: PosAlign.center, underline: true),
),
]);
], false);

bytes += generator.text('Text size 200%',
styles: PosStyles(
Expand Down
1 change: 0 additions & 1 deletion lib/src/capability_profile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
*/

import 'dart:convert' show json;
import 'dart:convert' show utf8;
import 'package:flutter/services.dart' show rootBundle;

class CodePage {
Expand Down
13 changes: 9 additions & 4 deletions lib/src/enums.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
*/

enum PosAlign { left, center, right }

enum PosCutMode { full, partial }

enum PosFontType { fontA, fontB }

enum PosDrawer { pin2, pin5 }

/// Choose image printing function
Expand All @@ -33,12 +36,14 @@ class PosTextSize {
}

class PaperSize {
const PaperSize._internal(this.value);
PaperSize._internal(this.value);
final int value;
static const mm58 = PaperSize._internal(1);
static const mm80 = PaperSize._internal(2);
int? custom;
static var mm58 = PaperSize._internal(1);
static var mm80 = PaperSize._internal(2);

int get width => value == PaperSize.mm58.value ? 372 : 558;
int get width =>
(custom != null ? custom! : (value == PaperSize.mm58.value ? 372 : 558));
}

class PosBeepDuration {
Expand Down
107 changes: 75 additions & 32 deletions lib/src/generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import 'package:hex/hex.dart';
import 'package:image/image.dart';
import 'package:gbk_codec/gbk_codec.dart';
import 'package:esc_pos_utils/esc_pos_utils.dart';
import 'enums.dart';
import 'commands.dart';

class Generator {
Expand Down Expand Up @@ -73,7 +72,10 @@ class Generator {
.replaceAll("»", '"')
.replaceAll(" ", ' ')
.replaceAll("•", '.');

if (!isKanji) {
text = text.replaceAll(
RegExp('[^A-Za-z0-9!"#\$%&\'\n()*+,./:;<=>?@\^_`{|}~-]'), ' ');
return latin1.encode(text);
} else {
return Uint8List.fromList(gbk_bytes.encode(text));
Expand All @@ -83,6 +85,9 @@ class Generator {
List _getLexemes(String text) {
final List<String> lexemes = [];
final List<bool> isLexemeChinese = [];
if (text.isEmpty) {
return [];
}
int start = 0;
int end = 0;
bool curLexemeChinese = _isChinese(text[0]);
Expand Down Expand Up @@ -144,17 +149,23 @@ class Generator {
final int heightPx = image.height;

// Create a black bottom layer
final biggerImage = copyResize(image, width: widthPx, height: heightPx);
fill(biggerImage, 0);
final biggerImage = copyResize(image,
width: widthPx, height: heightPx, interpolation: Interpolation.linear);
//fill(biggerImage, color: ColorRgb8(0, 0, 0));
fill(biggerImage, color: ColorRgb8(0, 0, 0));
// Insert source image into bigger one
drawImage(biggerImage, image, dstX: 0, dstY: 0);
compositeImage(biggerImage, image, dstX: 0, dstY: 0);

int left = 0;
final List<List<int>> blobs = [];

while (left < widthPx) {
final Image slice = copyCrop(biggerImage, left, 0, lineHeight, heightPx);
final Uint8List bytes = slice.getBytes(format: Format.luminance);
final Image slice = copyCrop(biggerImage,
x: left, y: 0, width: lineHeight, height: heightPx);
if (slice.numChannels > 2) grayscale(slice);
final imgBinary =
(slice.numChannels > 1) ? slice.convert(numChannels: 1) : slice;
final bytes = imgBinary.getBytes();
blobs.add(bytes);
left += lineHeight;
}
Expand All @@ -173,7 +184,7 @@ class Generator {

// R/G/B channels are same -> keep only one channel
final List<int> oneChannelBytes = [];
final List<int> buffer = image.getBytes(format: Format.rgba);
final List<int> buffer = image.getBytes();
for (int i = 0; i < buffer.length; i += 4) {
oneChannelBytes.add(buffer[i]);
}
Expand Down Expand Up @@ -350,6 +361,8 @@ class Generator {
}) {
List<int> bytes = [];
if (!containsChinese) {
text = text.replaceAll(
RegExp('[^A-Za-z0-9!"#\$%&\'\n()*+,./:;<=>?@\^_`{|}~-]'), ' ');
bytes += _text(
_encode(text, isKanji: containsChinese),
styles: styles,
Expand Down Expand Up @@ -459,7 +472,7 @@ class Generator {
///
/// A row contains up to 12 columns. A column has a width between 1 and 12.
/// Total width of columns in one row must be equal 12.
List<int> row(List<PosColumn> cols) {
List<int> row(List<PosColumn> cols, bool containChineese) {
List<int> bytes = [];
final isSumValid = cols.fold(0, (int sum, col) => sum + col.width) == 12;
if (!isSumValid) {
Expand All @@ -477,22 +490,38 @@ class Generator {
_colIndToPosition(colInd + cols[i].width) - spaceBetweenRows;
int maxCharactersNb = ((toPos - fromPos) / charWidth).floor();

if (!cols[i].containsChinese) {
if (true) {
// CASE 1: containsChinese = false
Uint8List encodedToPrint = cols[i].textEncoded != null
? cols[i].textEncoded!
: _encode(cols[i].text);
: _encode(cols[i].text, isKanji: containChineese);

// If the col's content is too long, split it to the next row
int realCharactersNb = encodedToPrint.length;
if (realCharactersNb > maxCharactersNb) {
// Print max possible and split to the next row
try {
while (String.fromCharCodes(
encodedToPrint.sublist(0, maxCharactersNb))[
String.fromCharCodes(
encodedToPrint.sublist(0, maxCharactersNb))
.length -
1] !=
" ") {
maxCharactersNb--;
if (maxCharactersNb == 0) {
maxCharactersNb = ((toPos - fromPos) / charWidth).floor();
break;
}
}
} catch (e) {}
Uint8List encodedToPrintNextRow =
encodedToPrint.sublist(maxCharactersNb);
encodedToPrint = encodedToPrint.sublist(0, maxCharactersNb);
isNextRow = true;
nextRow.add(PosColumn(
textEncoded: encodedToPrintNextRow,
containsChinese: containChineese,
width: cols[i].width,
styles: cols[i].styles));
} else {
Expand All @@ -501,12 +530,11 @@ class Generator {
text: '', width: cols[i].width, styles: cols[i].styles));
}
// end rows splitting
bytes += _text(
encodedToPrint,
styles: cols[i].styles,
colInd: colInd,
colWidth: cols[i].width,
);
bytes += _text(encodedToPrint,
styles: cols[i].styles,
colInd: colInd,
colWidth: cols[i].width,
isKanji: containChineese);
} else {
// CASE 1: containsChinese = true
// Split text into multiple lines if it too long
Expand All @@ -527,7 +555,7 @@ class Generator {
isNextRow = true;
nextRow.add(PosColumn(
text: toPrintNextRow,
containsChinese: true,
containsChinese: containChineese,
width: cols[i].width,
styles: cols[i].styles));
} else {
Expand All @@ -538,8 +566,8 @@ class Generator {

// Print current row
final list = _getLexemes(toPrint);
final List<String> lexemes = list[0];
final List<bool> isLexemeChinese = list[1];
final List<String> lexemes = list.isEmpty ? [] : list[0];
final List<bool> isLexemeChinese = list.isEmpty ? [] : list[1];

// Print each lexeme using codetable OR kanji
for (var j = 0; j < lexemes.length; ++j) {
Expand All @@ -559,28 +587,43 @@ class Generator {
bytes += emptyLines(1);

if (isNextRow) {
row(nextRow);
bytes += row(nextRow, containChineese);
}
return bytes;
}

/// Print an image using (ESC *) command
///
/// [image] is an instanse of class from [Image library](https://pub.dev/packages/image)
List<int> image(Image imgSrc, {PosAlign align = PosAlign.center}) {
List<int> image(Image imgSrc,
{PosAlign align = PosAlign.center, bool isDoubleDensity = true}) {
List<int> bytes = [];
// Image alignment
bytes += setStyles(PosStyles().copyWith(align: align));
bytes += setStyles(const PosStyles().copyWith(align: align));

Image image;
if (!isDoubleDensity) {
int size = 558 ~/ 2;
if (_paperSize == PaperSize.mm58) {
size = 375 ~/ 2;
} else if (_paperSize == PaperSize.mm80) {
size = 503 ~/ 2;
}

final Image image = Image.from(imgSrc); // make a copy
const bool highDensityHorizontal = true;
const bool highDensityVertical = true;
image =
copyResize(imgSrc, width: size, interpolation: Interpolation.linear);
} else {
image = Image.from(imgSrc); // make a copy
}

bool highDensityHorizontal = isDoubleDensity;
bool highDensityVertical = isDoubleDensity;

invert(image);
flip(image, Flip.horizontal);
final Image imageRotated = copyRotate(image, 270);
flipHorizontal(image);
final Image imageRotated = copyRotate(image, angle: 270);

const int lineHeight = highDensityVertical ? 3 : 1;
int lineHeight = highDensityVertical ? 3 : 1;
final List<List<int>> blobs = _toColumnFormat(imageRotated, lineHeight * 8);

// Compress according to line density
Expand All @@ -592,15 +635,15 @@ class Generator {
}

final int heightPx = imageRotated.height;
const int densityByte =
int densityByte =
(highDensityHorizontal ? 1 : 0) + (highDensityVertical ? 32 : 0);

final List<int> header = List.from(cBitImg.codeUnits);
header.add(densityByte);
header.addAll(_intLowHigh(heightPx, 2));

// Adjust line spacing (for 16-unit line feeds): ESC 3 0x10 (HEX: 0x1b 0x33 0x10)
bytes += [27, 51, 16];
bytes += [27, 51, 0];
for (int i = 0; i < blobs.length; ++i) {
bytes += List.from(header)
..addAll(blobs[i])
Expand Down Expand Up @@ -815,8 +858,8 @@ class Generator {
}) {
List<int> bytes = [];
final list = _getLexemes(text);
final List<String> lexemes = list[0];
final List<bool> isLexemeChinese = list[1];
final List<String> lexemes = list.isEmpty ? [] : list[0];
final List<bool> isLexemeChinese = list.isEmpty ? [] : list[1];

// Print each lexeme using codetable OR kanji
int? colInd = 0;
Expand Down
Loading