Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Word wrapping #74

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
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