Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 50 additions & 102 deletions lib/src/generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ import 'package:image/image.dart';
import 'commands.dart';

class Generator {
Generator(this._paperSize, this._profile,
{this.spaceBetweenRows = 5, this.codec = latin1});
Generator(this._paperSize, this._profile, {this.spaceBetweenRows = 5, this.codec = latin1});

// Ticket config
final PaperSize _paperSize;
Expand Down Expand Up @@ -55,21 +54,16 @@ class Generator {
if (styles.fontType != null) {
charsPerLine = _getMaxCharsPerLine(styles.fontType);
} else {
charsPerLine =
_maxCharsPerLine ?? _getMaxCharsPerLine(_styles.fontType);
charsPerLine = _maxCharsPerLine ?? _getMaxCharsPerLine(_styles.fontType);
}
}
return charsPerLine;
}

Uint8List _encode(String text, {bool isKanji = false}) {
// replace some non-ascii characters
text = text
.replaceAll("’", "'")
.replaceAll("´", "'")
.replaceAll("»", '"')
.replaceAll(" ", ' ')
.replaceAll("•", '.');
text =
text.replaceAll("’", "'").replaceAll("´", "'").replaceAll("»", '"').replaceAll(" ", ' ').replaceAll("•", '.');
if (!isKanji) {
return codec.encode(text);
} else {
Expand Down Expand Up @@ -119,8 +113,7 @@ class Generator {
throw Exception('Can only output 1-4 bytes');
}
if (value < 0 || value > maxInput) {
throw Exception(
'Number is too large. Can only output up to $maxInput in $bytesNb bytes');
throw Exception('Number is too large. Can only output up to $maxInput in $bytesNb bytes');
}

final List<int> res = <int>[];
Expand All @@ -144,8 +137,7 @@ class Generator {
final int heightPx = image.height;

// Create a black bottom layer
final biggerImage = copyResize(image,
width: widthPx, height: heightPx, interpolation: Interpolation.linear);
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
Expand All @@ -155,11 +147,9 @@ class Generator {
final List<List<int>> blobs = [];

while (left < widthPx) {
final Image slice = copyCrop(biggerImage,
x: left, y: 0, width: lineHeight, height: heightPx);
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 imgBinary = (slice.numChannels > 1) ? slice.convert(numChannels: 1) : slice;
final bytes = imgBinary.getBytes();
blobs.add(bytes);
left += lineHeight;
Expand All @@ -177,38 +167,27 @@ class Generator {
grayscale(image);
invert(image);

// R/G/B channels are same -> keep only one channel
// Calculate target width (divisible by 8)
final targetWidth = (widthPx + 7) & ~7; // More efficient than (widthPx + 8) - (widthPx % 8)
final missingPx = targetWidth - widthPx;

List<int> oneChannelBytes = [];
// Get image bytes and extract one channel
final List<int> buffer = image.getBytes(order: ChannelOrder.rgba);
for (int i = 0; i < buffer.length; i += 4) {
oneChannelBytes.add(buffer[i]);
final targetWidth = (widthPx + 8) - (widthPx % 8);
final missingPx = targetWidth - widthPx;
final extra = Uint8List(missingPx);

oneChannelBytes = List<int>.filled(heightPx * targetWidth, 0);

for (int i = 0; i < heightPx; i++) {
List<int> oneChannelBytes = [];

final pos =
(i * widthPx) + i * missingPx; // Corrected position calculation
oneChannelBytes.insertAll(pos, extra);
// Process each row
for (int y = 0; y < heightPx; y++) {
// Add pixels from the current row
for (int x = 0; x < widthPx; x++) {
final pixelIndex = (y * widthPx + x) * 4; // RGBA has 4 channels
oneChannelBytes.add(buffer[pixelIndex]); // R channel (since it's grayscale, R=G=B)
}
// Add padding pixels to make width divisible by 8
for (int p = 0; p < missingPx; p++) {
oneChannelBytes.add(0); // Add black pixels as padding
}
}

// Add some empty pixels at the end of each line (to make the width divisible by 8)
// if (widthPx % 8 != 0) {

// final targetWidth = (widthPx + 8) - (widthPx % 8);
// final missingPx = targetWidth - widthPx;
// final extra = Uint8List(missingPx);
// for (int i = 0; i < heightPx; i++) {
// final pos = (i * widthPx + widthPx) + i * missingPx;
// oneChannelBytes.insertAll(pos, extra);
// }
// }

// Pack bits into bytes
return _packBitsIntoBytes(oneChannelBytes);
}
Expand All @@ -234,8 +213,7 @@ class Generator {

/// Replaces a single bit in a 32-bit unsigned integer.
int _transformUint32Bool(int uint32, int shift, bool newValue) {
return ((0xFFFFFFFF ^ (0x1 << shift)) & uint32) |
((newValue ? 1 : 0) << shift);
return ((0xFFFFFFFF ^ (0x1 << shift)) & uint32) | ((newValue ? 1 : 0) << shift);
}
// ************************ (end) Internal helpers ************************

Expand All @@ -252,8 +230,7 @@ class Generator {

/// Clear the buffer and reset text styles
List<int> clearStyle() {
return setStyles(
const PosStyles(height: PosTextSize.size1, width: PosTextSize.size1));
return setStyles(const PosStyles(height: PosTextSize.size1, width: PosTextSize.size1));
}

/// Set global code table which will be used instead of the default printer's code table
Expand Down Expand Up @@ -286,9 +263,8 @@ class Generator {
List<int> setStyles(PosStyles styles, {bool isKanji = false}) {
List<int> bytes = [];
if (styles.align != _styles.align) {
bytes += codec.encode(styles.align == PosAlign.left
? cAlignLeft
: (styles.align == PosAlign.center ? cAlignCenter : cAlignRight));
bytes += codec.encode(
styles.align == PosAlign.left ? cAlignLeft : (styles.align == PosAlign.center ? cAlignCenter : cAlignRight));
_styles = _styles.copyWith(align: styles.align);
}

Expand All @@ -305,28 +281,23 @@ class Generator {
_styles = _styles.copyWith(reverse: styles.reverse);
}
if (styles.underline != _styles.underline) {
bytes +=
styles.underline ? cUnderline1dot.codeUnits : cUnderlineOff.codeUnits;
bytes += styles.underline ? cUnderline1dot.codeUnits : cUnderlineOff.codeUnits;
_styles = _styles.copyWith(underline: styles.underline);
}

// Set font
if (styles.fontType != null && styles.fontType != _styles.fontType) {
bytes += styles.fontType == PosFontType.fontB
? cFontB.codeUnits
: cFontA.codeUnits;
bytes += styles.fontType == PosFontType.fontB ? cFontB.codeUnits : cFontA.codeUnits;
_styles = _styles.copyWith(fontType: styles.fontType);
} else if (_font != null && _font != _styles.fontType) {
bytes += _font == PosFontType.fontB ? cFontB.codeUnits : cFontA.codeUnits;
_styles = _styles.copyWith(fontType: _font);
}

// Characters size
if (styles.height.value != _styles.height.value ||
styles.width.value != _styles.width.value) {
if (styles.height.value != _styles.height.value || styles.width.value != _styles.width.value) {
bytes += Uint8List.fromList(
List.from(cSizeGSn.codeUnits)
..add(PosTextSize.decSize(styles.height, styles.width)),
List.from(cSizeGSn.codeUnits)..add(PosTextSize.decSize(styles.height, styles.width)),
);
_styles = _styles.copyWith(height: styles.height, width: styles.width);
}
Expand All @@ -341,15 +312,12 @@ class Generator {
// Set local code table
if (styles.codeTable != null) {
bytes += Uint8List.fromList(
List.from(cCodeTable.codeUnits)
..add(_profile.getCodePageId(styles.codeTable)),
List.from(cCodeTable.codeUnits)..add(_profile.getCodePageId(styles.codeTable)),
);
_styles =
_styles.copyWith(align: styles.align, codeTable: styles.codeTable);
_styles = _styles.copyWith(align: styles.align, codeTable: styles.codeTable);
} else if (_codeTable != null) {
bytes += Uint8List.fromList(
List.from(cCodeTable.codeUnits)
..add(_profile.getCodePageId(_codeTable)),
List.from(cCodeTable.codeUnits)..add(_profile.getCodePageId(_codeTable)),
);
_styles = _styles.copyWith(align: styles.align, codeTable: _codeTable);
}
Expand Down Expand Up @@ -452,8 +420,7 @@ class Generator {
/// Beeps [n] times
///
/// Beep [duration] could be between 50 and 450 ms.
List<int> beep(
{int n = 3, PosBeepDuration duration = PosBeepDuration.beep450ms}) {
List<int> beep({int n = 3, PosBeepDuration duration = PosBeepDuration.beep450ms}) {
List<int> bytes = [];
if (n <= 0) {
return [];
Expand Down Expand Up @@ -495,37 +462,28 @@ class Generator {
List<PosColumn> nextRow = <PosColumn>[];

for (int i = 0; i < cols.length; ++i) {
int colInd =
cols.sublist(0, i).fold(0, (int sum, col) => sum + col.width);
int colInd = cols.sublist(0, i).fold(0, (int sum, col) => sum + col.width);
double charWidth = _getCharWidth(cols[i].styles);
double fromPos = _colIndToPosition(colInd);
final double toPos =
_colIndToPosition(colInd + cols[i].width) - spaceBetweenRows;
final double toPos = _colIndToPosition(colInd + cols[i].width) - spaceBetweenRows;
int maxCharactersNb = ((toPos - fromPos) / charWidth).floor();

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

// If the col's content is too long, split it to the next row
if (multiLine) {
int realCharactersNb = encodedToPrint.length;
if (realCharactersNb > maxCharactersNb) {
// Print max possible and split to the next row
Uint8List encodedToPrintNextRow =
encodedToPrint.sublist(maxCharactersNb);
Uint8List encodedToPrintNextRow = encodedToPrint.sublist(maxCharactersNb);
encodedToPrint = encodedToPrint.sublist(0, maxCharactersNb);
isNextRow = true;
nextRow.add(PosColumn(
textEncoded: encodedToPrintNextRow,
width: cols[i].width,
styles: cols[i].styles));
nextRow.add(PosColumn(textEncoded: encodedToPrintNextRow, width: cols[i].width, styles: cols[i].styles));
} else {
// Insert an empty col
nextRow.add(PosColumn(
text: '', width: cols[i].width, styles: cols[i].styles));
nextRow.add(PosColumn(text: '', width: cols[i].width, styles: cols[i].styles));
}
}
// end rows splitting
Expand Down Expand Up @@ -553,15 +511,11 @@ class Generator {

if (toPrintNextRow.isNotEmpty) {
isNextRow = true;
nextRow.add(PosColumn(
text: toPrintNextRow,
containsChinese: true,
width: cols[i].width,
styles: cols[i].styles));
nextRow.add(
PosColumn(text: toPrintNextRow, containsChinese: true, width: cols[i].width, styles: cols[i].styles));
} else {
// Insert an empty col
nextRow.add(PosColumn(
text: '', width: cols[i].width, styles: cols[i].styles));
nextRow.add(PosColumn(text: '', width: cols[i].width, styles: cols[i].styles));
}

// Print current row
Expand Down Expand Up @@ -596,8 +550,7 @@ class Generator {
/// Print an image using (ESC *) command
///
/// [image] is an instance of class from [Image library](https://pub.dev/packages/image)
List<int> image(Image imgSrc,
{PosAlign align = PosAlign.center, bool isDoubleDensity = true}) {
List<int> image(Image imgSrc, {PosAlign align = PosAlign.center, bool isDoubleDensity = true}) {
List<int> bytes = [];
// Image alignment
bytes += setStyles(const PosStyles().copyWith(align: align));
Expand All @@ -611,8 +564,7 @@ class Generator {
size = 503 ~/ 2;
}

image =
copyResize(imgSrc, width: size, interpolation: Interpolation.linear);
image = copyResize(imgSrc, width: size, interpolation: Interpolation.linear);
} else {
image = Image.from(imgSrc); // make a copy
}
Expand All @@ -636,8 +588,7 @@ class Generator {
}

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

final List<int> header = List.from(cBitImg.codeUnits);
header.add(densityByte);
Expand Down Expand Up @@ -676,8 +627,7 @@ class Generator {

if (imageFn == PosImageFn.bitImageRaster) {
// GS v 0
final int densityByte =
(highDensityVertical ? 0 : 1) + (highDensityHorizontal ? 0 : 2);
final int densityByte = (highDensityVertical ? 0 : 1) + (highDensityHorizontal ? 0 : 2);

final List<int> header = List.from(cRasterImg2.codeUnits);
header.add(densityByte); // m
Expand Down Expand Up @@ -825,15 +775,13 @@ class Generator {
}) {
List<int> bytes = [];
if (colInd != null) {
double charWidth =
_getCharWidth(styles, maxCharsPerLine: maxCharsPerLine);
double charWidth = _getCharWidth(styles, maxCharsPerLine: maxCharsPerLine);
double fromPos = _colIndToPosition(colInd);

// Align
if (colWidth != 12) {
// Update fromPos
final double toPos =
_colIndToPosition(colInd + colWidth) - spaceBetweenRows;
final double toPos = _colIndToPosition(colInd + colWidth) - spaceBetweenRows;
final double textLen = textBytes.length * charWidth;

if (styles.align == PosAlign.right) {
Expand Down