@bricks-soft/capacitor-thermal-printer 0.0.0-stage → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BricksSoftCapacitorThermalPrinter.podspec +17 -0
- package/LICENSE +201 -0
- package/Package.swift +13 -0
- package/README.md +80 -2
- package/android/build.gradle +40 -0
- package/android/src/main/AndroidManifest.xml +8 -0
- package/android/src/main/java/com/brickssoft/thermalprinter/ThermalPrinterPlugin.java +463 -0
- package/dist/esm/definitions.d.ts +73 -0
- package/dist/esm/definitions.js +1 -0
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +23 -0
- package/dist/esm/web.d.ts +13 -0
- package/dist/esm/web.js +33 -0
- package/dist/plugin.cjs.js +65 -0
- package/dist/plugin.js +68 -0
- package/ios/Sources/ThermalPrinter/EscPosBarcode.swift +121 -0
- package/ios/Sources/ThermalPrinter/EscPosBitmap.swift +178 -0
- package/ios/Sources/ThermalPrinter/EscPosBluetooth.swift +341 -0
- package/ios/Sources/ThermalPrinter/EscPosBluetoothConnection.swift +298 -0
- package/ios/Sources/ThermalPrinter/EscPosBluetoothRules.swift +78 -0
- package/ios/Sources/ThermalPrinter/EscPosCharsetEncoding.swift +115 -0
- package/ios/Sources/ThermalPrinter/EscPosCoreBluetoothLink.swift +287 -0
- package/ios/Sources/ThermalPrinter/EscPosDeviceConnection.swift +47 -0
- package/ios/Sources/ThermalPrinter/EscPosJavaCompat.swift +213 -0
- package/ios/Sources/ThermalPrinter/EscPosKnownPrinterStore.swift +44 -0
- package/ios/Sources/ThermalPrinter/EscPosPrinter.swift +55 -0
- package/ios/Sources/ThermalPrinter/EscPosPrinterCommands.swift +367 -0
- package/ios/Sources/ThermalPrinter/EscPosPrinterSize.swift +53 -0
- package/ios/Sources/ThermalPrinter/EscPosQRCode.swift +70 -0
- package/ios/Sources/ThermalPrinter/EscPosQueue.swift +30 -0
- package/ios/Sources/ThermalPrinter/EscPosTcpConnection.swift +265 -0
- package/ios/Sources/ThermalPrinter/PrinterTextParser.swift +622 -0
- package/ios/Sources/ThermalPrinter/ThermalPrinterPlugin.swift +85 -0
- package/ios/Sources/ThermalPrinter/ThermalPrinterService.swift +380 -0
- package/package.json +61 -4
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import Capacitor
|
|
2
|
+
import Foundation
|
|
3
|
+
|
|
4
|
+
@objc(ThermalPrinterPlugin)
|
|
5
|
+
public final class ThermalPrinterPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
6
|
+
public let identifier = "ThermalPrinterPlugin"
|
|
7
|
+
public let jsName = "ThermalPrinter"
|
|
8
|
+
public let pluginMethods: [CAPPluginMethod] = [
|
|
9
|
+
CAPPluginMethod(name: "listPrinters", returnType: CAPPluginReturnPromise),
|
|
10
|
+
CAPPluginMethod(name: "printFormattedText", returnType: CAPPluginReturnPromise),
|
|
11
|
+
CAPPluginMethod(name: "printFormattedTextAndCut", returnType: CAPPluginReturnPromise),
|
|
12
|
+
CAPPluginMethod(name: "getEncoding", returnType: CAPPluginReturnPromise),
|
|
13
|
+
CAPPluginMethod(name: "disconnectPrinter", returnType: CAPPluginReturnPromise),
|
|
14
|
+
CAPPluginMethod(name: "requestPermissions", returnType: CAPPluginReturnPromise),
|
|
15
|
+
CAPPluginMethod(name: "requestBTPermissions", returnType: CAPPluginReturnPromise),
|
|
16
|
+
CAPPluginMethod(name: "requestUSBPermissions", returnType: CAPPluginReturnPromise),
|
|
17
|
+
CAPPluginMethod(name: "bitmapToHexadecimalString", returnType: CAPPluginReturnPromise),
|
|
18
|
+
CAPPluginMethod(name: "bytesToHexadecimalString", returnType: CAPPluginReturnPromise),
|
|
19
|
+
]
|
|
20
|
+
private let service = ThermalPrinterService()
|
|
21
|
+
|
|
22
|
+
deinit {
|
|
23
|
+
service.cancelPendingRequests()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@objc public func listPrinters(_ call: CAPPluginCall) {
|
|
27
|
+
run("listPrinters", call)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@objc public func printFormattedText(_ call: CAPPluginCall) {
|
|
31
|
+
run("printFormattedText", call)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@objc public func printFormattedTextAndCut(_ call: CAPPluginCall) {
|
|
35
|
+
run("printFormattedTextAndCut", call)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@objc public func getEncoding(_ call: CAPPluginCall) {
|
|
39
|
+
run("getEncoding", call)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@objc public func disconnectPrinter(_ call: CAPPluginCall) {
|
|
43
|
+
run("disconnectPrinter", call)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@objc override public func requestPermissions(_ call: CAPPluginCall) {
|
|
47
|
+
run("requestPermissions", call)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@objc public func requestBTPermissions(_ call: CAPPluginCall) {
|
|
51
|
+
run("requestBTPermissions", call)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@objc public func requestUSBPermissions(_ call: CAPPluginCall) {
|
|
55
|
+
run("requestPermissions", call)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@objc public func bitmapToHexadecimalString(_ call: CAPPluginCall) {
|
|
59
|
+
run("bitmapToHexadecimalString", call)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@objc public func bytesToHexadecimalString(_ call: CAPPluginCall) {
|
|
63
|
+
guard let bytes = call.getArray("bytes", Int.self), bytes.allSatisfy({ (0...255).contains($0) }) else {
|
|
64
|
+
call.reject("bytes must contain PNG or JPEG bytes in the range 0...255")
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
var options = call.options as? [String: Any] ?? [:]
|
|
68
|
+
options["base64"] = Data(bytes.map(UInt8.init)).base64EncodedString()
|
|
69
|
+
run("bitmapToHexadecimalString", call, options: options)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private func run(_ action: String, _ call: CAPPluginCall, options: [String: Any]? = nil) {
|
|
73
|
+
service.execute(action, [options ?? (call.options as? [String: Any] ?? [:])]) { result in
|
|
74
|
+
switch result {
|
|
75
|
+
case .success(let payload):
|
|
76
|
+
call.resolve(payload.map { ["value": $0] } ?? [:])
|
|
77
|
+
case .error(let payload):
|
|
78
|
+
let data = payload as? [String: Any]
|
|
79
|
+
call.reject(data?["error"] as? String ?? String(describing: payload), nil, nil, data)
|
|
80
|
+
case .cancelled:
|
|
81
|
+
call.reject("Printer request cancelled")
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import CoreBluetooth
|
|
2
|
+
import Foundation
|
|
3
|
+
|
|
4
|
+
enum ThermalPrinterResult {
|
|
5
|
+
case success(Any?)
|
|
6
|
+
case error(Any)
|
|
7
|
+
case cancelled
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
struct ThermalPrinterFailure: Error {
|
|
11
|
+
let payload: Any
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
struct ThermalPrinterJSONError: Error {
|
|
15
|
+
let message: String
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
struct ThermalPrinterArguments {
|
|
19
|
+
let values: [String: Any]
|
|
20
|
+
|
|
21
|
+
init(_ values: [String: Any]) {
|
|
22
|
+
self.values = values
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
func has(_ key: String) -> Bool {
|
|
26
|
+
values[key] != nil
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
func getString(_ key: String) throws -> String {
|
|
30
|
+
guard let value = values[key] else {
|
|
31
|
+
throw ThermalPrinterJSONError(message: "No value for \(key)")
|
|
32
|
+
}
|
|
33
|
+
return ThermalPrinterArguments.javaString(value)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
func optString(_ key: String, _ fallback: String = "") -> String {
|
|
37
|
+
guard let value = values[key] else { return fallback }
|
|
38
|
+
return ThermalPrinterArguments.javaString(value)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
func optInt(_ key: String, _ fallback: Int) -> Int {
|
|
42
|
+
guard let value = values[key] else { return fallback }
|
|
43
|
+
if ThermalPrinterArguments.isBoolean(value) { return fallback }
|
|
44
|
+
if let number = value as? NSNumber { return JavaMath.toInt(number.doubleValue) }
|
|
45
|
+
if let string = value as? String, let parsed = JavaMath.parseDouble(string) { return JavaMath.toInt(parsed) }
|
|
46
|
+
return fallback
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
func optDouble(_ key: String, _ fallback: Double) -> Double {
|
|
50
|
+
guard let value = values[key] else { return fallback }
|
|
51
|
+
if ThermalPrinterArguments.isBoolean(value) { return fallback }
|
|
52
|
+
if let number = value as? NSNumber { return number.doubleValue }
|
|
53
|
+
if let string = value as? String, let parsed = JavaMath.parseDouble(string) { return parsed }
|
|
54
|
+
return fallback
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
func getDouble(_ key: String) throws -> Double {
|
|
58
|
+
guard let value = values[key] else {
|
|
59
|
+
throw ThermalPrinterJSONError(message: "No value for \(key)")
|
|
60
|
+
}
|
|
61
|
+
if !ThermalPrinterArguments.isBoolean(value) {
|
|
62
|
+
if let number = value as? NSNumber { return number.doubleValue }
|
|
63
|
+
if let string = value as? String, let parsed = JavaMath.parseDouble(string) { return parsed }
|
|
64
|
+
}
|
|
65
|
+
throw ThermalPrinterJSONError(message: "Value \(ThermalPrinterArguments.javaString(value)) at \(key) cannot be converted to double")
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
func optObject(_ key: String) -> ThermalPrinterArguments? {
|
|
69
|
+
guard let object = values[key] as? [String: Any] else { return nil }
|
|
70
|
+
return ThermalPrinterArguments(object)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private static func isBoolean(_ value: Any) -> Bool {
|
|
74
|
+
guard let number = value as? NSNumber else { return false }
|
|
75
|
+
return CFGetTypeID(number) == CFBooleanGetTypeID()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private static func javaString(_ value: Any) -> String {
|
|
79
|
+
if value is NSNull { return "null" }
|
|
80
|
+
if let string = value as? String { return string }
|
|
81
|
+
if let number = value as? NSNumber {
|
|
82
|
+
if isBoolean(number) { return number.boolValue ? "true" : "false" }
|
|
83
|
+
return number.stringValue
|
|
84
|
+
}
|
|
85
|
+
return String(describing: value)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
final class ThermalPrinterService {
|
|
90
|
+
private let queue = DispatchQueue(label: "de.paystory.thermal-printer.commands")
|
|
91
|
+
private var connections: [String: EscPosDeviceConnection] = [:]
|
|
92
|
+
private var bluetoothCentral: EscPosBluetoothCentral?
|
|
93
|
+
private let makeBluetoothCentral: () -> EscPosBluetoothCentral
|
|
94
|
+
private let makeTcpConnection: (String, Int) -> EscPosDeviceConnection
|
|
95
|
+
|
|
96
|
+
init(
|
|
97
|
+
makeBluetoothCentral: @escaping () -> EscPosBluetoothCentral = { EscPosBluetoothCentral() },
|
|
98
|
+
makeTcpConnection: @escaping (String, Int) -> EscPosDeviceConnection = { EscPosTcpConnection(address: $0, port: $1) }
|
|
99
|
+
) {
|
|
100
|
+
self.makeBluetoothCentral = makeBluetoothCentral
|
|
101
|
+
self.makeTcpConnection = makeTcpConnection
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
deinit {
|
|
105
|
+
bluetoothCentral?.cancelAuthorizationRequests()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private var bluetooth: EscPosBluetoothCentral {
|
|
109
|
+
if let central = bluetoothCentral {
|
|
110
|
+
return central
|
|
111
|
+
}
|
|
112
|
+
let central = makeBluetoothCentral()
|
|
113
|
+
bluetoothCentral = central
|
|
114
|
+
return central
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
func execute(_ action: String, _ arguments: [Any], completion: @escaping (ThermalPrinterResult) -> Void) {
|
|
118
|
+
queue.async {
|
|
119
|
+
if action == "requestBTPermissions", arguments.first is [String: Any] {
|
|
120
|
+
self.requestBTPermissions(completion)
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
completion(self.run(action, arguments))
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
func cancelPendingRequests() {
|
|
128
|
+
queue.async {
|
|
129
|
+
self.bluetoothCentral?.cancelAuthorizationRequests()
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
func run(_ action: String, _ arguments: [Any]) -> ThermalPrinterResult {
|
|
134
|
+
guard let object = arguments.first as? [String: Any] else {
|
|
135
|
+
return .error("Value at 0 is not a JSONObject")
|
|
136
|
+
}
|
|
137
|
+
let data = ThermalPrinterArguments(object)
|
|
138
|
+
do {
|
|
139
|
+
switch action {
|
|
140
|
+
case "listPrinters":
|
|
141
|
+
return try listPrinters(data)
|
|
142
|
+
case "printFormattedText", "printFormattedTextAndCut":
|
|
143
|
+
return try printFormattedText(action, data)
|
|
144
|
+
case "getEncoding":
|
|
145
|
+
return try getEncoding(data)
|
|
146
|
+
case "disconnectPrinter":
|
|
147
|
+
return try disconnectPrinter(data)
|
|
148
|
+
case "requestPermissions":
|
|
149
|
+
return try requestUSBPermissions(data)
|
|
150
|
+
case "bitmapToHexadecimalString":
|
|
151
|
+
return try bitmapToHexadecimalString(data)
|
|
152
|
+
default:
|
|
153
|
+
return .error("Invalid action")
|
|
154
|
+
}
|
|
155
|
+
} catch let failure as ThermalPrinterFailure {
|
|
156
|
+
return .error(failure.payload)
|
|
157
|
+
} catch let failure as ThermalPrinterJSONError {
|
|
158
|
+
return .error(failure.message)
|
|
159
|
+
} catch let failure as EscPosError {
|
|
160
|
+
return .error(["error": failure.message])
|
|
161
|
+
} catch {
|
|
162
|
+
return .error(["error": error.localizedDescription])
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private func requestBTPermissions(_ completion: @escaping (ThermalPrinterResult) -> Void) {
|
|
167
|
+
bluetooth.requestAuthorization { answer in
|
|
168
|
+
switch answer {
|
|
169
|
+
case .some(.allowedAlways):
|
|
170
|
+
completion(.success(["granted": true]))
|
|
171
|
+
case .none:
|
|
172
|
+
completion(.cancelled)
|
|
173
|
+
default:
|
|
174
|
+
completion(.error(["granted": false]))
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private func listPrinters(_ data: ThermalPrinterArguments) throws -> ThermalPrinterResult {
|
|
180
|
+
let type = try data.getString("type")
|
|
181
|
+
guard type == "bluetooth" else {
|
|
182
|
+
return .success([[String: Any]]())
|
|
183
|
+
}
|
|
184
|
+
try checkBluetooth()
|
|
185
|
+
let printers: [[String: Any]] = bluetooth.listDevices().map { device in
|
|
186
|
+
var printer: [String: Any] = [
|
|
187
|
+
"address": device.identifier.uuidString,
|
|
188
|
+
"type": 2,
|
|
189
|
+
]
|
|
190
|
+
if let name = device.name {
|
|
191
|
+
printer["name"] = name
|
|
192
|
+
}
|
|
193
|
+
if !device.services.isEmpty {
|
|
194
|
+
printer["features"] = device.services.map(\.uuidString)
|
|
195
|
+
}
|
|
196
|
+
return printer
|
|
197
|
+
}
|
|
198
|
+
return .success(printers)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
private func printFormattedText(_ action: String, _ data: ThermalPrinterArguments) throws -> ThermalPrinterResult {
|
|
202
|
+
try printFormattedText(action, data, printer: getPrinter(data))
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
func printFormattedText(_ action: String, _ data: ThermalPrinterArguments, printer: EscPosPrinter) throws -> ThermalPrinterResult {
|
|
206
|
+
do {
|
|
207
|
+
let dotsFeedPaper = data.has("mmFeedPaper")
|
|
208
|
+
? printer.mmToPx(Float(try data.getDouble("mmFeedPaper")))
|
|
209
|
+
: data.optInt("dotsFeedPaper", 20)
|
|
210
|
+
let text = try data.getString("text")
|
|
211
|
+
if action.hasSuffix("Cut") {
|
|
212
|
+
try printer.printFormattedTextAndCut(text, dotsFeedPaper: dotsFeedPaper)
|
|
213
|
+
} else {
|
|
214
|
+
try printer.printFormattedText(text, dotsFeedPaper: dotsFeedPaper)
|
|
215
|
+
}
|
|
216
|
+
return .success(nil)
|
|
217
|
+
} catch let failure as EscPosError {
|
|
218
|
+
printer.printer?.printerConnection.clearBuffer()
|
|
219
|
+
throw ThermalPrinterFailure(payload: ["error": failure.message])
|
|
220
|
+
} catch let failure as ThermalPrinterJSONError {
|
|
221
|
+
printer.printer?.printerConnection.clearBuffer()
|
|
222
|
+
throw ThermalPrinterFailure(payload: ["error": failure.message])
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
private func getEncoding(_ data: ThermalPrinterArguments) throws -> ThermalPrinterResult {
|
|
227
|
+
try encodingResult(getPrinter(data))
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
func encodingResult(_ printer: EscPosPrinter) -> ThermalPrinterResult {
|
|
231
|
+
guard let encoding = printer.encoding else {
|
|
232
|
+
return .success("null")
|
|
233
|
+
}
|
|
234
|
+
return .success([
|
|
235
|
+
"name": encoding.name,
|
|
236
|
+
"command": encoding.command.map { Int(Int8(bitPattern: $0)) },
|
|
237
|
+
])
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private func disconnectPrinter(_ data: ThermalPrinterArguments) throws -> ThermalPrinterResult {
|
|
241
|
+
let printer = try getPrinter(data)
|
|
242
|
+
printer.disconnectPrinter()
|
|
243
|
+
return .success(nil)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private func requestUSBPermissions(_ data: ThermalPrinterArguments) throws -> ThermalPrinterResult {
|
|
247
|
+
_ = try getPrinterConnection(data)
|
|
248
|
+
return .error(["error": "USB printers are not supported on iOS."])
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private func bitmapToHexadecimalString(_ data: ThermalPrinterArguments) throws -> ThermalPrinterResult {
|
|
252
|
+
let encoded = try data.getString("base64")
|
|
253
|
+
let payload: Substring
|
|
254
|
+
if let comma = encoded.firstIndex(of: ",") {
|
|
255
|
+
payload = encoded[encoded.index(after: comma)...]
|
|
256
|
+
} else {
|
|
257
|
+
payload = Substring(encoded)
|
|
258
|
+
}
|
|
259
|
+
guard let bytes = ThermalPrinterService.decodeBase64(payload) else {
|
|
260
|
+
return .error(["error": "bad base-64"])
|
|
261
|
+
}
|
|
262
|
+
let printer = try getPrinter(data)
|
|
263
|
+
guard let bitmap = EscPosBitmap.decode(bytes) else {
|
|
264
|
+
return .error(["error": "Unable to decode the image."])
|
|
265
|
+
}
|
|
266
|
+
return .success(PrinterTextParserImg.bitmapToHexadecimalString(printer, bitmap))
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
static func decodeBase64(_ text: Substring) -> Data? {
|
|
270
|
+
var cleaned = text.filter { !$0.isWhitespace }
|
|
271
|
+
let remainder = cleaned.count % 4
|
|
272
|
+
if remainder == 1 {
|
|
273
|
+
return nil
|
|
274
|
+
}
|
|
275
|
+
if remainder > 0 {
|
|
276
|
+
cleaned.append(String(repeating: "=", count: 4 - remainder))
|
|
277
|
+
}
|
|
278
|
+
return Data(base64Encoded: cleaned)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
func getPrinter(_ data: ThermalPrinterArguments) throws -> EscPosPrinter {
|
|
282
|
+
let connection = try getPrinterConnection(data)
|
|
283
|
+
return try makePrinter(connection: connection, data: data)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
func makePrinter(connection: EscPosDeviceConnection, data: ThermalPrinterArguments) throws -> EscPosPrinter {
|
|
287
|
+
var charsetEncoding: EscPosCharsetEncoding?
|
|
288
|
+
if let charset = data.optObject("charsetEncoding") {
|
|
289
|
+
charsetEncoding = EscPosCharsetEncoding(
|
|
290
|
+
name: charset.optString("charsetName", "windows-1252"),
|
|
291
|
+
escPosCharsetId: charset.optInt("charsetId", 16)
|
|
292
|
+
)
|
|
293
|
+
}
|
|
294
|
+
do {
|
|
295
|
+
return try EscPosPrinter(
|
|
296
|
+
connection: connection,
|
|
297
|
+
printerDpi: data.optInt("printerDpi", 203),
|
|
298
|
+
printerWidthMM: Float(data.optDouble("printerWidthMM", 48)),
|
|
299
|
+
printerNbrCharactersPerLine: data.optInt("printerNbrCharactersPerLine", 32),
|
|
300
|
+
charsetEncoding: charsetEncoding
|
|
301
|
+
)
|
|
302
|
+
} catch let failure as EscPosError {
|
|
303
|
+
throw ThermalPrinterFailure(payload: ["error": failure.message])
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
private func getPrinterConnection(_ data: ThermalPrinterArguments) throws -> EscPosDeviceConnection {
|
|
308
|
+
let type = try data.getString("type")
|
|
309
|
+
let id = try data.getString("id")
|
|
310
|
+
let hashKey = "\(type)-\(id)"
|
|
311
|
+
guard let connection = try getDevice(
|
|
312
|
+
hashKey: hashKey,
|
|
313
|
+
type: type,
|
|
314
|
+
id: data.optString("id"),
|
|
315
|
+
address: data.optString("address"),
|
|
316
|
+
port: data.optInt("port", 9100)
|
|
317
|
+
) else {
|
|
318
|
+
throw ThermalPrinterFailure(payload: [
|
|
319
|
+
"error": "Device not found or not connected!",
|
|
320
|
+
"type": type,
|
|
321
|
+
"id": id,
|
|
322
|
+
])
|
|
323
|
+
}
|
|
324
|
+
connections[hashKey] = connection
|
|
325
|
+
return connection
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
private func getDevice(hashKey: String, type: String, id: String, address: String, port: Int) throws -> EscPosDeviceConnection? {
|
|
329
|
+
if let existing = connections[hashKey] {
|
|
330
|
+
if existing.isConnected {
|
|
331
|
+
return existing
|
|
332
|
+
}
|
|
333
|
+
connections.removeValue(forKey: hashKey)
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
switch type {
|
|
337
|
+
case "bluetooth":
|
|
338
|
+
try checkBluetooth()
|
|
339
|
+
if id == "first" {
|
|
340
|
+
for peripheral in bluetooth.candidatesForFirst() {
|
|
341
|
+
let connection = bluetooth.connection(for: peripheral)
|
|
342
|
+
if (try? connection.connect()) != nil {
|
|
343
|
+
return connection
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return nil
|
|
347
|
+
}
|
|
348
|
+
guard let peripheral = bluetooth.findPeripheral(id: id, address: address) else {
|
|
349
|
+
return nil
|
|
350
|
+
}
|
|
351
|
+
return bluetooth.connection(for: peripheral)
|
|
352
|
+
case "tcp":
|
|
353
|
+
return makeTcpConnection(address, port)
|
|
354
|
+
default:
|
|
355
|
+
return nil
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
private func checkBluetooth() throws {
|
|
360
|
+
if bluetooth.authorization == .notDetermined {
|
|
361
|
+
_ = bluetooth.waitForAuthorization()
|
|
362
|
+
}
|
|
363
|
+
switch bluetooth.authorization {
|
|
364
|
+
case .denied, .restricted, .notDetermined:
|
|
365
|
+
throw ThermalPrinterFailure(payload: ["error": "Missing permission for Bluetooth!"])
|
|
366
|
+
default:
|
|
367
|
+
break
|
|
368
|
+
}
|
|
369
|
+
switch bluetooth.waitForState() {
|
|
370
|
+
case .poweredOn:
|
|
371
|
+
return
|
|
372
|
+
case .unsupported:
|
|
373
|
+
throw ThermalPrinterFailure(payload: ["error": "Device doesn't support Bluetooth!"])
|
|
374
|
+
case .unauthorized:
|
|
375
|
+
throw ThermalPrinterFailure(payload: ["error": "Missing permission for Bluetooth!"])
|
|
376
|
+
default:
|
|
377
|
+
throw ThermalPrinterFailure(payload: ["error": "Device not enabled Bluetooth!"])
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,63 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bricks-soft/capacitor-thermal-printer",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "8.0.0",
|
|
4
|
+
"description": "ESC/POS receipt printing over Bluetooth, TCP and USB for Capacitor.",
|
|
5
|
+
"main": "dist/plugin.cjs.js",
|
|
6
|
+
"module": "dist/esm/index.js",
|
|
7
|
+
"types": "dist/esm/index.d.ts",
|
|
8
|
+
"unpkg": "dist/plugin.js",
|
|
9
|
+
"files": [
|
|
10
|
+
"android/src/main/",
|
|
11
|
+
"android/build.gradle",
|
|
12
|
+
"dist/",
|
|
13
|
+
"ios/Sources/",
|
|
14
|
+
"Package.swift",
|
|
15
|
+
"BricksSoftCapacitorThermalPrinter.podspec"
|
|
16
|
+
],
|
|
17
|
+
"author": "Bricks Soft",
|
|
18
|
+
"license": "Apache-2.0",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/bricks-soft/capacitor-thermal-printer.git"
|
|
22
|
+
},
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/bricks-soft/capacitor-thermal-printer/issues"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/bricks-soft/capacitor-thermal-printer#readme",
|
|
27
|
+
"keywords": [
|
|
28
|
+
"capacitor",
|
|
29
|
+
"plugin",
|
|
30
|
+
"thermal",
|
|
31
|
+
"printer",
|
|
32
|
+
"escpos"
|
|
33
|
+
],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"clean": "rimraf dist",
|
|
36
|
+
"build": "npm run clean && tsc && rollup -c rollup.config.mjs",
|
|
37
|
+
"test": "npm run build && node --test test/*.mjs",
|
|
38
|
+
"prepare": "npm run build"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@capacitor/core": "8.5.2",
|
|
42
|
+
"@capacitor/android": "8.5.2",
|
|
43
|
+
"@capacitor/cli": "8.5.2",
|
|
44
|
+
"@capacitor/ios": "8.5.2",
|
|
45
|
+
"rimraf": "^6.1.3",
|
|
46
|
+
"rollup": "^4.53.2",
|
|
47
|
+
"typescript": "~5.9.3"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"@capacitor/core": ">=8.0.0 <9.0.0"
|
|
51
|
+
},
|
|
52
|
+
"capacitor": {
|
|
53
|
+
"android": {
|
|
54
|
+
"src": "android"
|
|
55
|
+
},
|
|
56
|
+
"ios": {
|
|
57
|
+
"src": "ios"
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"access": "public"
|
|
62
|
+
}
|
|
63
|
+
}
|