Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
E
esc_pos_print_plus
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Dio Maulana
esc_pos_print_plus
Commits
78cbb375
Commit
78cbb375
authored
Jan 16, 2020
by
Andrey
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added PrinterNetworkManager. Updated examples.
parent
6fb75107
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
257 additions
and
687 deletions
+257
-687
.flutter-plugins-dependencies
example/discover_printers/.flutter-plugins-dependencies
+1
-0
Flutter.podspec
example/discover_printers/ios/Flutter/Flutter.podspec
+18
-0
flutter_export_environment.sh
...scover_printers/ios/Flutter/flutter_export_environment.sh
+2
-1
Podfile.lock
example/discover_printers/ios/Podfile.lock
+8
-2
main.dart
example/discover_printers/lib/main.dart
+78
-59
pubspec.yaml
example/discover_printers/pubspec.yaml
+1
-1
example.dart
example/example.dart
+75
-59
esc_pos_printer.dart
lib/esc_pos_printer.dart
+1
-1
enums.dart
lib/src/enums.dart
+23
-0
printer.dart
lib/src/printer.dart
+0
-560
printer_bluetooth_manager.dart
lib/src/printer_bluetooth_manager.dart
+1
-4
printer_network_manager.dart
lib/src/printer_network_manager.dart
+49
-0
No files found.
example/discover_printers/.flutter-plugins-dependencies
0 → 100644
View file @
78cbb375
{"_info":"// This is a generated file; do not edit or check into version control.","dependencyGraph":[{"name":"flutter_bluetooth_basic","dependencies":[]},{"name":"wifi","dependencies":[]}]}
\ No newline at end of file
example/discover_printers/ios/Flutter/Flutter.podspec
0 → 100644
View file @
78cbb375
#
# NOTE: This podspec is NOT to be published. It is only used as a local source!
#
Pod
::
Spec
.
new
do
|
s
|
s
.
name
=
'Flutter'
s
.
version
=
'1.0.0'
s
.
summary
=
'High-performance, high-fidelity mobile apps.'
s
.
description
=
<<-
DESC
Flutter provides an easy and productive way to build and deploy high-performance mobile apps for Android and iOS.
DESC
s
.
homepage
=
'https://flutter.io'
s
.
license
=
{
:type
=>
'MIT'
}
s
.
author
=
{
'Flutter Dev Team'
=>
'flutter-dev@googlegroups.com'
}
s
.
source
=
{
:git
=>
'https://github.com/flutter/engine'
,
:tag
=>
s
.
version
.
to_s
}
s
.
ios
.
deployment_target
=
'8.0'
s
.
vendored_frameworks
=
'Flutter.framework'
end
example/discover_printers/ios/Flutter/flutter_export_environment.sh
View file @
78cbb375
...
...
@@ -2,9 +2,10 @@
# This is a generated file; do not edit or check into version control.
export
"FLUTTER_ROOT=/Users/andrey/Documents/flutter"
export
"FLUTTER_APPLICATION_PATH=/Users/andrey/Documents/Tablemi_v2/esc_pos_printer/example/discover_printers"
export
"FLUTTER_TARGET=lib/main.dart"
export
"FLUTTER_TARGET=
/Users/andrey/Documents/Tablemi_v2/esc_pos_printer/example/discover_printers/
lib/main.dart"
export
"FLUTTER_BUILD_DIR=build"
export
"SYMROOT=
${
SOURCE_ROOT
}
/../build/ios"
export
"FLUTTER_FRAMEWORK_DIR=/Users/andrey/Documents/flutter/bin/cache/artifacts/engine/ios"
export
"FLUTTER_BUILD_NAME=1.0.0"
export
"FLUTTER_BUILD_NUMBER=1"
export
"TRACK_WIDGET_CREATION=true"
example/discover_printers/ios/Podfile.lock
View file @
78cbb375
PODS:
- Flutter (1.0.0)
- flutter_bluetooth_basic (0.0.1):
- Flutter
- wifi (0.0.1):
- Flutter
DEPENDENCIES:
- Flutter (from `.symlinks/flutter/ios`)
- flutter_bluetooth_basic (from `.symlinks/plugins/flutter_bluetooth_basic/ios`)
- wifi (from `.symlinks/plugins/wifi/ios`)
EXTERNAL SOURCES:
Flutter:
:path: ".symlinks/flutter/ios"
flutter_bluetooth_basic:
:path: ".symlinks/plugins/flutter_bluetooth_basic/ios"
wifi:
:path: ".symlinks/plugins/wifi/ios"
SPEC CHECKSUMS:
Flutter: 58dd7d1b27887414a370fcccb9e645c08ffd7a6a
Flutter: 0e3d915762c693b495b44d77113d4970485de6ec
flutter_bluetooth_basic: 0e4e27e22b50b3a25cc1d1e131953feb4af414f4
wifi: d7d77c94109e36c4175d845f0a5964eadba71060
PODFILE CHECKSUM: aff02bfeed411c636180d6812254b2daeea14d09
COCOAPODS: 1.
7.5
COCOAPODS: 1.
8.4
example/discover_printers/lib/main.dart
View file @
78cbb375
...
...
@@ -63,7 +63,7 @@ class _MyHomePageState extends State<MyHomePage> {
}
print
(
'subnet:
\t
$subnet
, port:
\t
$port
'
);
final
stream
=
NetworkAnalyzer
.
discover
(
subnet
,
port
);
final
stream
=
NetworkAnalyzer
.
discover
2
(
subnet
,
port
);
stream
.
listen
((
NetworkAddress
addr
)
{
if
(
addr
.
exists
)
{
...
...
@@ -87,64 +87,83 @@ class _MyHomePageState extends State<MyHomePage> {
});
}
void
testPrint
(
String
printerIp
,
BuildContext
ctx
)
{
Printer
.
connect
(
printerIp
,
port:
int
.
parse
(
portController
.
text
),
timeout:
Duration
(
seconds:
5
))
.
then
((
printer
)
async
{
printer
.
println
(
'Normal text'
);
printer
.
println
(
'Bold text'
,
styles:
PosStyles
(
bold:
true
));
printer
.
println
(
'Reverse text'
,
styles:
PosStyles
(
reverse:
true
));
printer
.
println
(
'Underlined text'
,
styles:
PosStyles
(
underline:
true
),
linesAfter:
1
);
printer
.
println
(
'Align left'
,
styles:
PosStyles
(
align:
PosTextAlign
.
left
));
printer
.
println
(
'Align center'
,
styles:
PosStyles
(
align:
PosTextAlign
.
center
));
printer
.
println
(
'Align right'
,
styles:
PosStyles
(
align:
PosTextAlign
.
right
),
linesAfter:
1
);
printer
.
printRow
([
PosColumn
(
text:
'col3'
,
width:
3
,
styles:
PosStyles
(
align:
PosTextAlign
.
center
,
underline:
true
),
),
PosColumn
(
text:
'col6'
,
width:
6
,
styles:
PosStyles
(
align:
PosTextAlign
.
center
,
underline:
true
),
),
PosColumn
(
text:
'col3'
,
width:
3
,
styles:
PosStyles
(
align:
PosTextAlign
.
center
,
underline:
true
),
),
]);
printer
.
println
(
'Text size 200%'
,
styles:
PosStyles
(
height:
PosTextSize
.
size2
,
width:
PosTextSize
.
size2
,
));
// Print image
final
ByteData
data
=
await
rootBundle
.
load
(
'assets/logo.png'
);
final
Uint8List
bytes
=
data
.
buffer
.
asUint8List
();
final
Image
image
=
decodeImage
(
bytes
);
printer
.
printImage
(
image
);
printer
.
cut
();
printer
.
disconnect
();
final
snackBar
=
SnackBar
(
content:
Text
(
'Success'
,
textAlign:
TextAlign
.
center
));
Scaffold
.
of
(
ctx
).
showSnackBar
(
snackBar
);
}).
catchError
((
dynamic
e
)
{
print
(
'exception'
);
final
snackBar
=
SnackBar
(
content:
Text
(
'Fail'
,
textAlign:
TextAlign
.
center
));
Scaffold
.
of
(
ctx
).
showSnackBar
(
snackBar
);
});
Future
<
Ticket
>
testTicket
()
async
{
final
Ticket
ticket
=
Ticket
(
PaperSize
.
mm80
);
ticket
.
text
(
'Regular: aA bB cC dD eE fF gG hH iI jJ kK lL mM nN oO pP qQ rR sS tT uU vV wW xX yY zZ'
);
ticket
.
text
(
'Special 1: àÀ èÈ éÉ ûÛ üÜ çÇ ôÔ'
,
styles:
PosStyles
(
codeTable:
PosCodeTable
.
westEur
));
ticket
.
text
(
'Special 2: blåbærgrød'
,
styles:
PosStyles
(
codeTable:
PosCodeTable
.
westEur
));
ticket
.
text
(
'Bold text'
,
styles:
PosStyles
(
bold:
true
));
ticket
.
text
(
'Reverse text'
,
styles:
PosStyles
(
reverse:
true
));
ticket
.
text
(
'Underlined text'
,
styles:
PosStyles
(
underline:
true
),
linesAfter:
1
);
ticket
.
text
(
'Align left'
,
styles:
PosStyles
(
align:
PosTextAlign
.
left
));
ticket
.
text
(
'Align center'
,
styles:
PosStyles
(
align:
PosTextAlign
.
center
));
ticket
.
text
(
'Align right'
,
styles:
PosStyles
(
align:
PosTextAlign
.
right
),
linesAfter:
1
);
ticket
.
row
([
PosColumn
(
text:
'col3'
,
width:
3
,
styles:
PosStyles
(
align:
PosTextAlign
.
center
,
underline:
true
),
),
PosColumn
(
text:
'col6'
,
width:
6
,
styles:
PosStyles
(
align:
PosTextAlign
.
center
,
underline:
true
),
),
PosColumn
(
text:
'col3'
,
width:
3
,
styles:
PosStyles
(
align:
PosTextAlign
.
center
,
underline:
true
),
),
]);
ticket
.
text
(
'Text size 200%'
,
styles:
PosStyles
(
height:
PosTextSize
.
size2
,
width:
PosTextSize
.
size2
,
));
// Print image
final
ByteData
data
=
await
rootBundle
.
load
(
'assets/logo.png'
);
final
Uint8List
bytes
=
data
.
buffer
.
asUint8List
();
final
Image
image
=
decodeImage
(
bytes
);
ticket
.
image
(
image
);
// Print image using an alternative (obsolette) command
// ticket.imageRaster(image);
// Print barcode
final
List
<
int
>
barData
=
[
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
0
,
4
];
ticket
.
barcode
(
Barcode
.
upcA
(
barData
));
// Print mixed (chinese + latin) text. Only for printers supporting Kanji mode
// ticket.text(
// 'hello ! 中文字 # world @ éphémère &',
// styles: PosStyles(codeTable: PosCodeTable.westEur),
// containsChinese: true,
// );
ticket
.
feed
(
2
);
ticket
.
cut
();
return
ticket
;
}
void
testPrint
(
String
printerIp
,
BuildContext
ctx
)
async
{
final
PrinterNetworkManager
printerManager
=
PrinterNetworkManager
();
printerManager
.
selectPrinter
(
printerIp
,
port:
9100
);
final
PosPrintResult
res
=
await
printerManager
.
printTicket
(
await
testTicket
());
final
snackBar
=
SnackBar
(
content:
Text
(
PosPrintResult
.
msg
(
res
),
textAlign:
TextAlign
.
center
));
Scaffold
.
of
(
ctx
).
showSnackBar
(
snackBar
);
}
@override
...
...
example/discover_printers/pubspec.yaml
View file @
78cbb375
...
...
@@ -20,7 +20,7 @@ dependencies:
flutter
:
sdk
:
flutter
wifi
:
^0.1.5
ping_discover_network
:
^0.
1.2
ping_discover_network
:
^0.
2.0+1
image
:
^2.1.4
esc_pos_printer
:
path
:
../../
...
...
example/example.dart
View file @
78cbb375
import
'dart:io'
;
import
'package:esc_pos_printer/esc_pos_printer.dart'
;
import
'dart:typed_data'
;
import
'package:flutter/services.dart'
;
import
'package:image/image.dart'
;
import
'package:esc_pos_printer/esc_pos_printer.dart'
;
void
main
(
)
{
// To discover existing printers in your subnet, consider using
void
main
(
)
async
{
final
PrinterNetworkManager
printerManager
=
PrinterNetworkManager
();
// To discover network printers in your subnet, consider using
// ping_discover_network package (https://pub.dev/packages/ping_discover_network).
// Note that most of ESC/POS printers by default listen on port 9100.
Printer
.
connect
(
'192.168.0.123'
,
port:
9100
).
then
((
printer
)
{
printer
.
println
(
'Regular: aA bB cC dD eE fF gG hH iI jJ kK lL mM nN oO pP qQ rR sS tT uU vV wW xX yY zZ'
);
printer
.
println
(
'Special 1: àÀ èÈ éÉ ûÛ üÜ çÇ ôÔ'
,
styles:
PosStyles
(
codeTable:
PosCodeTable
.
westEur
));
printer
.
println
(
'Special 2: blåbærgrød'
,
styles:
PosStyles
(
codeTable:
PosCodeTable
.
westEur
));
// Note that most of ESC/POS printers are available on port 9100 by default.
printerManager
.
selectPrinter
(
'192.168.0.123'
,
port:
9100
);
final
PosPrintResult
res
=
await
printerManager
.
printTicket
(
await
testTicket
());
print
(
'Print result:
${PosPrintResult.msg(res)}
'
);
}
Future
<
Ticket
>
testTicket
()
async
{
final
Ticket
ticket
=
Ticket
(
PaperSize
.
mm80
);
ticket
.
text
(
'Regular: aA bB cC dD eE fF gG hH iI jJ kK lL mM nN oO pP qQ rR sS tT uU vV wW xX yY zZ'
);
ticket
.
text
(
'Special 1: àÀ èÈ éÉ ûÛ üÜ çÇ ôÔ'
,
styles:
PosStyles
(
codeTable:
PosCodeTable
.
westEur
));
ticket
.
text
(
'Special 2: blåbærgrød'
,
styles:
PosStyles
(
codeTable:
PosCodeTable
.
westEur
));
ticket
.
text
(
'Bold text'
,
styles:
PosStyles
(
bold:
true
));
ticket
.
text
(
'Reverse text'
,
styles:
PosStyles
(
reverse:
true
));
ticket
.
text
(
'Underlined text'
,
styles:
PosStyles
(
underline:
true
),
linesAfter:
1
);
ticket
.
text
(
'Align left'
,
styles:
PosStyles
(
align:
PosTextAlign
.
left
));
ticket
.
text
(
'Align center'
,
styles:
PosStyles
(
align:
PosTextAlign
.
center
));
ticket
.
text
(
'Align right'
,
styles:
PosStyles
(
align:
PosTextAlign
.
right
),
linesAfter:
1
);
ticket
.
row
([
PosColumn
(
text:
'col3'
,
width:
3
,
styles:
PosStyles
(
align:
PosTextAlign
.
center
,
underline:
true
),
),
PosColumn
(
text:
'col6'
,
width:
6
,
styles:
PosStyles
(
align:
PosTextAlign
.
center
,
underline:
true
),
),
PosColumn
(
text:
'col3'
,
width:
3
,
styles:
PosStyles
(
align:
PosTextAlign
.
center
,
underline:
true
),
),
]);
ticket
.
text
(
'Text size 200%'
,
styles:
PosStyles
(
height:
PosTextSize
.
size2
,
width:
PosTextSize
.
size2
,
));
printer
.
println
(
'Bold text'
,
styles:
PosStyles
(
bold:
true
));
printer
.
println
(
'Reverse text'
,
styles:
PosStyles
(
reverse:
true
));
printer
.
println
(
'Underlined text'
,
styles:
PosStyles
(
underline:
true
),
linesAfter:
1
);
printer
.
println
(
'Align left'
,
styles:
PosStyles
(
align:
PosTextAlign
.
left
));
printer
.
println
(
'Align center'
,
styles:
PosStyles
(
align:
PosTextAlign
.
center
));
printer
.
println
(
'Align right'
,
styles:
PosStyles
(
align:
PosTextAlign
.
right
),
linesAfter:
1
);
printer
.
printRow
([
PosColumn
(
text:
'col3'
,
width:
3
,
styles:
PosStyles
(
align:
PosTextAlign
.
center
,
underline:
true
),
),
PosColumn
(
text:
'col6'
,
width:
6
,
styles:
PosStyles
(
align:
PosTextAlign
.
center
,
underline:
true
),
),
PosColumn
(
text:
'col3'
,
width:
3
,
styles:
PosStyles
(
align:
PosTextAlign
.
center
,
underline:
true
),
),
]);
printer
.
println
(
'Text size 200%'
,
styles:
PosStyles
(
height:
PosTextSize
.
size2
,
width:
PosTextSize
.
size2
,
));
// Print image
final
ByteData
data
=
await
rootBundle
.
load
(
'assets/logo.png'
);
final
Uint8List
bytes
=
data
.
buffer
.
asUint8List
();
final
Image
image
=
decodeImage
(
bytes
);
ticket
.
image
(
image
);
// Print image using an alternative (obsolette) command
// ticket.imageRaster(image);
// Print image
const
String
filename
=
'./logo.png'
;
final
Image
image
=
decodeImage
(
File
(
filename
).
readAsBytesSync
());
printer
.
printImage
(
image
);
// Print image using an alternative (obsolette) command
// printer.printImageRaster(image);
// Print barcode
final
List
<
int
>
barData
=
[
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
0
,
4
];
ticket
.
barcode
(
Barcode
.
upcA
(
barData
));
// Print barcode
final
List
<
int
>
barData
=
[
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
0
,
4
];
printer
.
printBarcode
(
Barcode
.
upcA
(
barData
));
// Print mixed (chinese + latin) text. Only for printers supporting Kanji mode
// ticket.text(
// 'hello ! 中文字 # world @ éphémère &',
// styles: PosStyles(codeTable: PosCodeTable.westEur),
// containsChinese: true,
// );
// Print mixed (chinese + latin) text. Only for printers supporting Kanji mode
// printer.println(
// 'hello ! 中文字 # world @ éphémère &',
// styles: PosStyles(codeTable: PosCodeTable.westEur),
// );
ticket
.
feed
(
2
);
printer
.
cut
();
printer
.
disconnect
();
});
ticket
.
cut
();
return
ticket
;
}
lib/esc_pos_printer.dart
View file @
78cbb375
...
...
@@ -11,6 +11,6 @@ export './src/barcode.dart';
export
'./src/enums.dart'
;
export
'./src/pos_column.dart'
;
export
'./src/pos_styles.dart'
;
export
'./src/printer.dart'
;
export
'./src/printer_bluetooth_manager.dart'
;
export
'./src/printer_network_manager.dart'
;
export
'./src/ticket.dart'
;
lib/src/enums.dart
View file @
78cbb375
...
...
@@ -10,6 +10,29 @@ enum PosTextAlign { left, center, right }
enum
PosCutMode
{
full
,
partial
}
enum
PosFontType
{
fontA
,
fontB
}
class
PosPrintResult
{
const
PosPrintResult
.
_internal
(
this
.
value
);
final
int
value
;
static
const
success
=
PosPrintResult
.
_internal
(
1
);
static
const
timeout
=
PosPrintResult
.
_internal
(
2
);
static
const
printerNotSelected
=
PosPrintResult
.
_internal
(
3
);
static
const
ticketEmpty
=
PosPrintResult
.
_internal
(
4
);
static
String
msg
(
PosPrintResult
val
)
{
if
(
val
==
PosPrintResult
.
success
)
{
return
'Success'
;
}
else
if
(
val
==
PosPrintResult
.
timeout
)
{
return
'Error. Printer connection timeout'
;
}
else
if
(
val
==
PosPrintResult
.
printerNotSelected
)
{
return
'Error. Printer not selected'
;
}
else
if
(
val
==
PosPrintResult
.
ticketEmpty
)
{
return
'Error. Ticket is empty'
;
}
else
{
return
'Unknown error'
;
}
}
}
class
PosTextSize
{
const
PosTextSize
.
_internal
(
this
.
value
);
final
int
value
;
...
...
lib/src/printer.dart
deleted
100644 → 0
View file @
6fb75107
/*
* esc_pos_printer
* Created by Andrey Ushakov
*
* Copyright (c) 2019-2020. All rights reserved.
* See LICENSE for distribution and usage details.
*/
import
'dart:convert'
;
import
'dart:io'
;
import
'dart:typed_data'
;
import
'package:gbk_codec/gbk_codec.dart'
;
import
'package:hex/hex.dart'
;
import
'package:image/image.dart'
;
import
'barcode.dart'
;
import
'commands.dart'
;
import
'enums.dart'
;
import
'pos_column.dart'
;
import
'pos_styles.dart'
;
/// Network printer
class
Printer
{
Printer
.
_internal
(
this
.
_socket
)
{
reset
();
}
final
Socket
_socket
;
PosCodeTable
_codeTable
;
/// Creates a new socket connection to the network printer.
///
/// The argument [timeout] is used to specify the maximum allowed time to wait
/// for a connection to be established.
static
Future
<
Printer
>
connect
(
String
host
,
{
int
port
=
9100
,
Duration
timeout
,
})
{
return
Socket
.
connect
(
host
,
port
,
timeout:
timeout
).
then
((
socket
)
{
return
Printer
.
_internal
(
socket
);
});
}
/// Disconnect from the printer
void
disconnect
()
{
_socket
.
destroy
();
}
/// Set global code table which will be used instead of the default printer's code table
void
setGlobalCodeTable
(
PosCodeTable
codeTable
)
{
_codeTable
=
codeTable
;
if
(
codeTable
!=
null
)
{
_socket
.
add
(
Uint8List
.
fromList
(
List
.
from
(
cCodeTable
.
codeUnits
)..
add
(
codeTable
.
value
),
),
);
}
}
double
_colIndToPosition
(
int
colInd
)
{
return
colInd
==
0
?
0
:
(
512
*
colInd
/
11
-
1
);
}
/// Generic print for internal use
///
/// [colInd] range: 0..11
void
_print
(
String
text
,
{
PosStyles
styles
=
const
PosStyles
(),
int
colInd
=
0
,
bool
kanjiOff
=
true
,
int
colWidth
=
12
,
})
{
const
charLen
=
11.625
;
// 48 symbols per line
double
fromPos
=
_colIndToPosition
(
colInd
);
// Align
if
(
colWidth
==
12
)
{
_socket
.
write
(
styles
.
align
==
PosTextAlign
.
left
?
cAlignLeft
:
(
styles
.
align
==
PosTextAlign
.
center
?
cAlignCenter
:
cAlignRight
));
}
else
{
final
double
toPos
=
_colIndToPosition
(
colInd
+
colWidth
)
-
5
;
final
double
textLen
=
text
.
length
*
charLen
;
if
(
styles
.
align
==
PosTextAlign
.
right
)
{
fromPos
=
toPos
-
textLen
;
}
else
if
(
styles
.
align
==
PosTextAlign
.
center
)
{
fromPos
=
fromPos
+
(
toPos
-
fromPos
)
/
2
-
textLen
/
2
;
}
}
final
hexStr
=
fromPos
.
round
().
toRadixString
(
16
).
padLeft
(
3
,
'0'
);
final
hexPair
=
HEX
.
decode
(
hexStr
);
_socket
.
write
(
styles
.
bold
?
cBoldOn
:
cBoldOff
);
_socket
.
write
(
styles
.
turn90
?
cTurn90On
:
cTurn90Off
);
_socket
.
write
(
styles
.
reverse
?
cReverseOn
:
cReverseOff
);
_socket
.
write
(
styles
.
underline
?
cUnderline1dot
:
cUnderlineOff
);
_socket
.
write
(
styles
.
fontType
==
PosFontType
.
fontA
?
cFontA
:
cFontB
);
// Text size
_socket
.
add
(
Uint8List
.
fromList
(
List
.
from
(
cSizeGSn
.
codeUnits
)
..
add
(
PosTextSize
.
decSize
(
styles
.
height
,
styles
.
width
)),
),
);
// Position
_socket
.
add
(
Uint8List
.
fromList
(
List
.
from
(
cPos
.
codeUnits
)..
addAll
([
hexPair
[
1
],
hexPair
[
0
]]),
),
);
// Cancel Kanji mode
if
(
kanjiOff
)
{
_socket
.
write
(
cKanjiOff
);
}
else
{
_socket
.
write
(
cKanjiOn
);
}
// Set local code table
if
(
styles
.
codeTable
!=
null
)
{
_socket
.
add
(
Uint8List
.
fromList
(
List
.
from
(
cCodeTable
.
codeUnits
)..
add
(
styles
.
codeTable
.
value
),
),
);
}
if
(
kanjiOff
)
{
_socket
.
add
(
latin1
.
encode
(
text
));
}
else
{
_socket
.
add
(
gbk_bytes
.
encode
(
text
));
}
}
/// Sens raw command(s)
void
sendRaw
(
List
<
int
>
cmd
,
{
bool
kanjiOff
=
true
})
{
if
(
kanjiOff
)
{
_socket
.
write
(
cKanjiOff
);
}
_socket
.
add
(
Uint8List
.
fromList
(
cmd
));
}
/// Prints one line of styled text
void
println
(
String
text
,
{
PosStyles
styles
=
const
PosStyles
(),
int
linesAfter
=
0
,
bool
containsChinese
=
false
,
})
{
if
(!
containsChinese
)
{
_print
(
text
,
styles:
styles
,
kanjiOff:
!
containsChinese
,
);
_socket
.
writeln
();
emptyLines
(
linesAfter
);
reset
();
}
else
{
_printlnMixedKanji
(
text
,
styles:
styles
,
linesAfter:
linesAfter
);
}
}
/// Break text into chinese/non-chinese lexemes
List
_getLexemes
(
String
text
)
{
bool
_isChinese
(
String
ch
)
{
return
ch
.
codeUnitAt
(
0
)
>
255
?
true
:
false
;
}
final
List
<
String
>
lexemes
=
[];
final
List
<
bool
>
isLexemeChinese
=
[];
int
start
=
0
;
int
end
=
0
;
bool
curLexemeChinese
=
_isChinese
(
text
[
0
]);
for
(
var
i
=
1
;
i
<
text
.
length
;
++
i
)
{
if
(
curLexemeChinese
==
_isChinese
(
text
[
i
]))
{
end
+=
1
;
}
else
{
lexemes
.
add
(
text
.
substring
(
start
,
end
+
1
));
isLexemeChinese
.
add
(
curLexemeChinese
);
start
=
i
;
end
=
i
;
curLexemeChinese
=
!
curLexemeChinese
;
}
}
lexemes
.
add
(
text
.
substring
(
start
,
end
+
1
));
isLexemeChinese
.
add
(
curLexemeChinese
);
return
<
dynamic
>[
lexemes
,
isLexemeChinese
];
}
/// Prints one line of styled mixed (chinese and latin symbols) text
void
_printlnMixedKanji
(
String
text
,
{
PosStyles
styles
=
const
PosStyles
(),
int
linesAfter
=
0
,
})
{
final
list
=
_getLexemes
(
text
);
final
List
<
String
>
lexemes
=
list
[
0
];
final
List
<
bool
>
isLexemeChinese
=
list
[
1
];
// Print each lexeme using codetable OR kanji
for
(
var
i
=
0
;
i
<
lexemes
.
length
;
++
i
)
{
_print
(
lexemes
[
i
],
styles:
styles
,
kanjiOff:
!
isLexemeChinese
[
i
],
);
}
_socket
.
writeln
();
emptyLines
(
linesAfter
);
reset
();
}
/// Print selected code table.
///
/// If [codeTable] is null, global code table is used.
/// If global code table is null, default printer code table is used.
void
printCodeTable
({
PosCodeTable
codeTable
})
{
_socket
.
write
(
cKanjiOff
);
if
(
codeTable
!=
null
)
{
_socket
.
add
(
Uint8List
.
fromList
(
List
.
from
(
cCodeTable
.
codeUnits
)..
add
(
codeTable
.
value
),
),
);
}
final
List
<
int
>
list
=
[];
for
(
int
i
=
0
;
i
<
256
;
i
++)
{
list
.
add
(
i
);
}
_socket
.
add
(
Uint8List
.
fromList
(
list
),
);
// Back to initial code table
setGlobalCodeTable
(
_codeTable
);
}
/// Print a row.
///
/// 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.
void
printRow
(
List
<
PosColumn
>
cols
)
{
final
validSum
=
cols
.
fold
(
0
,
(
int
sum
,
col
)
=>
sum
+
col
.
width
)
==
12
;
if
(!
validSum
)
{
throw
Exception
(
'Total columns width must be equal to 12'
);
}
for
(
int
i
=
0
;
i
<
cols
.
length
;
++
i
)
{
final
colInd
=
cols
.
sublist
(
0
,
i
).
fold
(
0
,
(
int
sum
,
col
)
=>
sum
+
col
.
width
);
if
(!
cols
[
i
].
containsChinese
)
{
_print
(
cols
[
i
].
text
,
styles:
cols
[
i
].
styles
,
colInd:
colInd
,
colWidth:
cols
[
i
].
width
,
);
}
else
{
final
list
=
_getLexemes
(
cols
[
i
].
text
);
final
List
<
String
>
lexemes
=
list
[
0
];
final
List
<
bool
>
isLexemeChinese
=
list
[
1
];
// Print each lexeme using codetable OR kanji
for
(
var
j
=
0
;
j
<
lexemes
.
length
;
++
j
)
{
_print
(
lexemes
[
j
],
styles:
cols
[
i
].
styles
,
colInd:
colInd
,
colWidth:
cols
[
i
].
width
,
kanjiOff:
!
isLexemeChinese
[
j
],
);
}
}
}
_socket
.
writeln
();
reset
();
}
/// Beeps [n] times
///
/// Beep [duration] could be between 50 and 450 ms.
void
beep
({
int
n
=
3
,
PosBeepDuration
duration
=
PosBeepDuration
.
beep450ms
})
{
if
(
n
<=
0
)
{
return
;
}
int
beepCount
=
n
;
if
(
beepCount
>
9
)
{
beepCount
=
9
;
}
_socket
.
add
(
Uint8List
.
fromList
(
List
.
from
(
cBeep
.
codeUnits
)..
addAll
([
beepCount
,
duration
.
value
]),
),
);
beep
(
n:
n
-
9
,
duration:
duration
);
}
/// Clear the buffer and reset text styles
void
reset
()
{
_socket
.
write
(
cInit
);
setGlobalCodeTable
(
_codeTable
);
}
/// Skips [n] lines
///
/// Similar to [feed] but uses an alternative command
void
emptyLines
(
int
n
)
{
if
(
n
>
0
)
{
_socket
.
write
(
List
.
filled
(
n
,
'
\n
'
).
join
());
}
}
/// Skips [n] lines
///
/// Similar to [emptyLines] but uses an alternative command
void
feed
(
int
n
)
{
if
(
n
>=
0
&&
n
<=
255
)
{
_socket
.
add
(
Uint8List
.
fromList
(
List
.
from
(
cFeedN
.
codeUnits
)..
add
(
n
),
),
);
}
}
/// Reverse feed for [n] lines (if supported by the priner)
void
reverseFeed
(
int
n
)
{
_socket
.
add
(
Uint8List
.
fromList
(
List
.
from
(
cReverseFeedN
.
codeUnits
)..
add
(
n
),
),
);
}
/// Cut the paper
///
/// [mode] is used to define the full or partial cut (if supported by the priner)
void
cut
({
PosCutMode
mode
=
PosCutMode
.
full
})
{
_socket
.
write
(
'
\n\n\n\n\n
'
);
if
(
mode
==
PosCutMode
.
partial
)
{
_socket
.
write
(
cCutPart
);
}
else
{
_socket
.
write
(
cCutFull
);
}
}
/// Generate multiple bytes for a number: In lower and higher parts, or more parts as needed.
///
/// [value] Input number
/// [bytesNb] The number of bytes to output (1 - 4)
List
<
int
>
_intLowHigh
(
int
value
,
int
bytesNb
)
{
final
dynamic
maxInput
=
256
<<
(
bytesNb
*
8
)
-
1
;
if
(
bytesNb
<
1
||
bytesNb
>
4
)
{
throw
Exception
(
'Can only output 1-4 bytes'
);
}
if
(
value
<
0
||
value
>
maxInput
)
{
throw
Exception
(
'Number too large. Can only output up to
$maxInput
in
$bytesNb
bytes'
);
}
final
List
<
int
>
res
=
<
int
>[];
int
buf
=
value
;
for
(
int
i
=
0
;
i
<
bytesNb
;
++
i
)
{
res
.
add
(
buf
%
256
);
buf
=
buf
~/
256
;
}
return
res
;
}
/// 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
);
}
/// Merges each 8 values (bits) into one byte
List
<
int
>
_packBitsIntoBytes
(
List
<
int
>
bytes
)
{
const
pxPerLine
=
8
;
final
List
<
int
>
res
=
<
int
>[];
const
threshold
=
127
;
// set the greyscale -> b/w threshold here
for
(
int
i
=
0
;
i
<
bytes
.
length
;
i
+=
pxPerLine
)
{
int
newVal
=
0
;
for
(
int
j
=
0
;
j
<
pxPerLine
;
j
++)
{
newVal
=
_transformUint32Bool
(
newVal
,
pxPerLine
-
j
,
bytes
[
i
+
j
]
>
threshold
,
);
}
res
.
add
(
newVal
~/
2
);
}
return
res
;
}
/// Print image using (ESC *) command
///
/// [image] is an instanse of class from [Image library](https://pub.dev/packages/image)
void
printImage
(
Image
imgSrc
)
{
final
Image
image
=
Image
.
from
(
imgSrc
);
// make a copy
const
bool
highDensityHorizontal
=
true
;
const
bool
highDensityVertical
=
true
;
invert
(
image
);
flip
(
image
,
Flip
.
horizontal
);
final
Image
imageRotated
=
copyRotate
(
image
,
270
);
const
int
lineHeight
=
highDensityVertical
?
3
:
1
;
final
List
<
List
<
int
>>
blobs
=
_toColumnFormat
(
imageRotated
,
lineHeight
*
8
);
// Compress according to line density
// Line height contains 8 or 24 pixels of src image
// Each blobs[i] contains greyscale bytes [0-255]
// const int pxPerLine = 24 ~/ lineHeight;
for
(
int
blobInd
=
0
;
blobInd
<
blobs
.
length
;
blobInd
++)
{
blobs
[
blobInd
]
=
_packBitsIntoBytes
(
blobs
[
blobInd
]);
}
final
int
heightPx
=
imageRotated
.
height
;
const
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)
sendRaw
([
27
,
51
,
16
]);
for
(
int
i
=
0
;
i
<
blobs
.
length
;
++
i
)
{
sendRaw
(
List
.
from
(
header
)..
addAll
(
blobs
[
i
])..
addAll
(
'
\n
'
.
codeUnits
));
}
// Reset line spacing: ESC 2 (HEX: 0x1b 0x32)
sendRaw
([
27
,
50
]);
}
/// Extract slices of an image as equal-sized blobs of column-format data.
///
/// [image] Image to extract from
/// [lineHeight] Printed line height in dots
List
<
List
<
int
>>
_toColumnFormat
(
Image
imgSrc
,
int
lineHeight
)
{
final
Image
image
=
Image
.
from
(
imgSrc
);
// make a copy
// Determine new width: closest integer that is divisible by lineHeight
final
int
widthPx
=
(
image
.
width
+
lineHeight
)
-
(
image
.
width
%
lineHeight
);
final
int
heightPx
=
image
.
height
;
// Create a black bottom layer
final
biggerImage
=
copyResize
(
image
,
width:
widthPx
,
height:
heightPx
);
fill
(
biggerImage
,
0
);
// Insert source image into bigger one
drawImage
(
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
);
blobs
.
add
(
bytes
);
left
+=
lineHeight
;
}
return
blobs
;
}
/// Print image using (GS v 0) obsolete command
///
/// [image] is an instanse of class from [Image library](https://pub.dev/packages/image)
void
printImageRaster
(
Image
imgSrc
,
{
bool
highDensityHorizontal
=
true
,
bool
highDensityVertical
=
true
,
})
{
final
Image
image
=
Image
.
from
(
imgSrc
);
// make a copy
final
int
widthPx
=
image
.
width
;
final
int
heightPx
=
image
.
height
;
final
int
widthBytes
=
(
widthPx
+
7
)
~/
8
;
final
int
densityByte
=
(
highDensityVertical
?
0
:
1
)
+
(
highDensityHorizontal
?
0
:
2
);
final
List
<
int
>
header
=
List
.
from
(
cRasterImg
.
codeUnits
);
header
.
add
(
densityByte
);
header
.
addAll
(
_intLowHigh
(
widthBytes
,
2
));
header
.
addAll
(
_intLowHigh
(
heightPx
,
2
));
grayscale
(
image
);
invert
(
image
);
// R/G/B channels are same -> keep only one channel
final
List
<
int
>
oneChannelBytes
=
[];
final
List
<
int
>
buffer
=
image
.
getBytes
(
format:
Format
.
rgba
);
for
(
int
i
=
0
;
i
<
buffer
.
length
;
i
+=
4
)
{
oneChannelBytes
.
add
(
buffer
[
i
]);
}
// Add some empty pixels at the end of each line (to make the width divisible by 8)
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
final
List
<
int
>
res
=
_packBitsIntoBytes
(
oneChannelBytes
);
sendRaw
(
List
.
from
(
header
)..
addAll
(
res
));
}
/// Print barcode
///
/// [width] range and units are different depending on the printer model.
/// [height] range: 1 - 255. The units depend on the printer model.
/// Width, height, font, text position settings are effective until performing of ESC @, reset or power-off.
void
printBarcode
(
Barcode
barcode
,
{
int
width
,
int
height
,
BarcodeFont
font
,
BarcodeText
textPos
=
BarcodeText
.
below
,
})
{
// Set text position
sendRaw
(
cBarcodeSelectPos
.
codeUnits
+
[
textPos
.
value
]);
// Set font
if
(
font
!=
null
)
{
sendRaw
(
cBarcodeSelectFont
.
codeUnits
+
[
font
.
value
]);
}
// Set width
if
(
width
!=
null
&&
width
>=
0
)
{
sendRaw
(
cBarcodeSetW
.
codeUnits
+
[
width
]);
}
// Set height
if
(
height
!=
null
&&
height
>=
1
&&
height
<=
255
)
{
sendRaw
(
cBarcodeSetH
.
codeUnits
+
[
height
]);
}
// Print barcode
final
header
=
cBarcodePrint
.
codeUnits
+
[
barcode
.
type
.
value
];
sendRaw
(
header
+
barcode
.
data
+
[
0
]);
}
}
lib/src/printer_bluetooth_manager.dart
View file @
78cbb375
...
...
@@ -94,7 +94,6 @@ class PrinterBluetoothManager {
_bluetoothManager
.
state
.
listen
((
state
)
async
{
switch
(
state
)
{
case
BluetoothManager
.
CONNECTED
:
// print('********************* CONNECTED');
// To avoid double call
if
(!
_isConnected
)
{
await
_bluetoothManager
.
writeData
(
bytes
);
...
...
@@ -102,14 +101,12 @@ class PrinterBluetoothManager {
}
// TODO sending disconnect signal should be event-based
_runDelayed
(
3
).
then
((
dynamic
v
)
async
{
// print('DISCONNECTING......');
await
_bluetoothManager
.
disconnect
();
_isPrinting
=
false
;
});
_isConnected
=
true
;
break
;
case
BluetoothManager
.
DISCONNECTED
:
// print('********************* DISCONNECTED');
_isConnected
=
false
;
break
;
default
:
...
...
@@ -127,7 +124,7 @@ class PrinterBluetoothManager {
}
Future
<
void
>
printTicket
(
Ticket
ticket
)
async
{
if
(
ticket
.
bytes
.
isNotEmpty
)
{
if
(
ticket
!=
null
&&
ticket
.
bytes
.
isNotEmpty
)
{
final
Future
<
void
>
res
=
writeBytes
(
ticket
.
bytes
);
return
res
;
}
else
{
...
...
lib/src/printer_network_manager.dart
0 → 100644
View file @
78cbb375
/*
* esc_pos_printer
* Created by Andrey Ushakov
*
* Copyright (c) 2019-2020. All rights reserved.
* See LICENSE for distribution and usage details.
*/
import
'dart:io'
;
import
'./enums.dart'
;
import
'./ticket.dart'
;
/// Printer Network Manager
class
PrinterNetworkManager
{
String
_host
;
int
_port
;
Duration
_timeout
;
/// Select a network printer
///
/// [timeout] is used to specify the maximum allowed time to wait
/// for a connection to be established.
void
selectPrinter
(
String
host
,
{
int
port
=
9100
,
Duration
timeout
=
const
Duration
(
seconds:
5
),
})
{
_host
=
host
;
_port
=
port
;
_timeout
=
timeout
;
}
Future
<
PosPrintResult
>
printTicket
(
Ticket
ticket
)
{
if
(
_host
==
null
||
_port
==
null
)
{
return
Future
<
PosPrintResult
>.
value
(
PosPrintResult
.
printerNotSelected
);
}
else
if
(
ticket
==
null
||
ticket
.
bytes
.
isEmpty
)
{
return
Future
<
PosPrintResult
>.
value
(
PosPrintResult
.
ticketEmpty
);
}
return
Socket
.
connect
(
_host
,
_port
,
timeout:
_timeout
)
.
then
((
Socket
socket
)
{
socket
.
add
(
ticket
.
bytes
);
socket
.
destroy
();
return
Future
<
PosPrintResult
>.
value
(
PosPrintResult
.
success
);
}).
catchError
((
dynamic
e
)
{
return
Future
<
PosPrintResult
>.
value
(
PosPrintResult
.
timeout
);
});
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment