@rdlabo/capacitor-brotherprint 8.1.1 → 8.2.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/docs/print.md ADDED
@@ -0,0 +1,36 @@
1
+ # Print
2
+
3
+ `printImage` sends a base64 image (MIME type removed) to a Brother printer. Call this after [Installation](/docs/installation). Discover a printer with [Search](/docs/search) and register [Events](/docs/events) for print results before printing.
4
+
5
+ Prepare a real image yourself (for example encode a PNG/JPEG from your app and strip any `data:...;base64,` prefix). Use `port` and `channelInfo` from the `BRLMChannelResult` you retained from `onPrinterAvailable`. Pick a `modelName` / `labelName` that matches your device and the [supported models](/docs/readme#supported-models) table.
6
+
7
+ ```typescript
8
+ import {
9
+ BrotherPrint,
10
+ BRLMPrinterLabelName,
11
+ BRLMPrinterModelName,
12
+ } from '@rdlabo/capacitor-brotherprint';
13
+ import type { BRLMChannelResult, BRLMPrintOptions } from '@rdlabo/capacitor-brotherprint';
14
+
15
+ const printImage = async (printer: BRLMChannelResult, encodedImage: string) => {
16
+ const options: BRLMPrintOptions = {
17
+ modelName: BRLMPrinterModelName.QL_820NWB,
18
+ labelName: BRLMPrinterLabelName.RollW62,
19
+ encodedImage,
20
+ numberOfCopies: 1,
21
+ autoCut: true,
22
+ port: printer.port,
23
+ channelInfo: printer.channelInfo,
24
+ };
25
+
26
+ await BrotherPrint.printImage(options);
27
+ };
28
+ ```
29
+
30
+ See the demo for a complete page:
31
+
32
+ https://github.com/rdlabo-dev/capacitor-brotherprint/blob/v8.1.1/demo/src/app/home/home.page.ts
33
+
34
+ <!-- !::printImage:: -->
35
+
36
+ <!-- !::BRLMPrintOptions:: -->
package/docs/search.md ADDED
@@ -0,0 +1,106 @@
1
+ # Search
2
+
3
+ Search finds nearby Brother printers. Results arrive on `onPrinterAvailable`. Call this after [Installation](/docs/installation). Register the listener before `search`, keep the discovered `BRLMChannelResult` (especially `channelInfo` and `port`), then continue to [Print](/docs/print). Full event list: [Events](/docs/events).
4
+
5
+ ## search
6
+
7
+ Register `onPrinterAvailable`, retain the channel, then start a Wi-Fi search. The `search` call itself returns `void`.
8
+
9
+ ```typescript
10
+ import type { PluginListenerHandle } from '@capacitor/core';
11
+ import {
12
+ BrotherPrint,
13
+ BrotherPrintEventsEnum,
14
+ BRLMPrinterPort,
15
+ } from '@rdlabo/capacitor-brotherprint';
16
+ import type { BRLMChannelResult } from '@rdlabo/capacitor-brotherprint';
17
+
18
+ let discovered: BRLMChannelResult | undefined;
19
+ let availableHandle: PluginListenerHandle | undefined;
20
+
21
+ const searchWifiPrinters = async () => {
22
+ if (!availableHandle) {
23
+ availableHandle = await BrotherPrint.addListener(
24
+ BrotherPrintEventsEnum.onPrinterAvailable,
25
+ (printer) => {
26
+ discovered = printer;
27
+ console.log('channelInfo', printer.channelInfo);
28
+ },
29
+ );
30
+ }
31
+
32
+ await BrotherPrint.search({
33
+ port: BRLMPrinterPort.wifi,
34
+ searchDuration: 15, // seconds
35
+ });
36
+ };
37
+
38
+ const stopSearching = async () => {
39
+ try {
40
+ await BrotherPrint.cancelSearchWiFiPrinter();
41
+ } finally {
42
+ await availableHandle?.remove();
43
+ availableHandle = undefined;
44
+ }
45
+ };
46
+ ```
47
+
48
+ Call `searchWifiPrinters` from the search button and await `stopSearching` when leaving the screen.
49
+
50
+ On iOS, `bluetooth` first lists connected MFi printers. If none are connected, the app displays the system Bluetooth accessory picker so you can select and pair a printer. The search promise completes after the picker callback; picker errors reject the promise.
51
+
52
+ For BLE-capable printers, use `port: BRLMPrinterPort.bluetoothLowEnergy`. On iOS this uses `startBLESearch`, without the Bluetooth accessory picker. Pass the discovered printer's `channelInfo` (BLE local name) unchanged to `isChannelAvailable` or `printImage`. BLE search errors reject the search promise. QL-820NWB/QL-820NWBc do not support BLE printing; use `bluetooth` or `wifi` for these models.
53
+
54
+ On Android, pair a Bluetooth printer in the system settings before calling `search` with `bluetooth`; the SDK lists paired printers and does not provide the iOS accessory picker. Bluetooth and BLE searches resolve after the search finishes, or reject on SDK errors. Android 12 and later request Nearby devices permissions; Android 11 and earlier request location permission for BLE. `isChannelAvailable` returns `false` when Bluetooth permission is missing.
55
+
56
+ `searchDuration` applies to `wifi` and `bluetoothLowEnergy`. `usb` is Android only. If nothing is found, you get no error and no printers. Signatures are on the [API](/docs/api#brlmsearchoption) page.
57
+
58
+ On Android, Bluetooth Classic searches return paired devices. To include only devices that report the Bluetooth Imaging/Printer class:
59
+
60
+ ```typescript
61
+ await BrotherPrint.search({
62
+ port: BRLMPrinterPort.bluetooth,
63
+ searchDuration: 15,
64
+ bluetoothPrintersOnly: true,
65
+ });
66
+ ```
67
+
68
+ `bluetoothPrintersOnly` defaults to `false`, preserving the unfiltered results. It is ignored on iOS and for other ports, including BLE. The filter does not depend on device names and does not identify Brother products: other manufacturers' printers can still appear. Printers with a missing or non-printer Bluetooth class are excluded when enabled.
69
+
70
+ ## isChannelAvailable
71
+
72
+ If you saved the last `BRLMChannelResult`, check whether that channel is still usable before [Print](/docs/print).
73
+
74
+ ```typescript
75
+ import { BrotherPrint } from '@rdlabo/capacitor-brotherprint';
76
+ import type { BRLMChannelResult } from '@rdlabo/capacitor-brotherprint';
77
+
78
+ const checkChannel = async (lastPrinter: BRLMChannelResult) => {
79
+ const { result } = await BrotherPrint.isChannelAvailable(lastPrinter);
80
+ if (!result) {
81
+ await BrotherPrint.search({
82
+ port: lastPrinter.port,
83
+ searchDuration: 15,
84
+ });
85
+ }
86
+ };
87
+ ```
88
+
89
+ <!-- !::isChannelAvailable:: -->
90
+
91
+ <!-- !::isChannelAvailableResult:: -->
92
+
93
+ <!-- !::BRLMChannelResult:: -->
94
+
95
+ ## cancelSearchWiFiPrinter / cancelSearchBluetoothPrinter
96
+
97
+ Use these to stop an active search before its timeout, including when leaving the screen.
98
+
99
+ ```typescript
100
+ import { BrotherPrint } from '@rdlabo/capacitor-brotherprint';
101
+
102
+ await BrotherPrint.cancelSearchWiFiPrinter();
103
+ await BrotherPrint.cancelSearchBluetoothPrinter();
104
+ ```
105
+
106
+ See [API](/docs/api#cancelsearchwifiprinter) for the cancellation signatures.
package/docs/usage.md ADDED
@@ -0,0 +1,76 @@
1
+ # Usage
2
+
3
+ The following Angular component shows how to set up listeners, search for printers, and print a base64-encoded image.
4
+
5
+ ```typescript
6
+ @Component({
7
+ selector: 'brother-print',
8
+ templateUrl: 'brother.component.html',
9
+ styleUrls: ['brother.component.scss'],
10
+ })
11
+ export class BrotherComponent implements OnInit, OnDestroy {
12
+ readonly #listenerHandlers: PluginListenerHandle[] = [];
13
+ readonly printers = signal<BRLMChannelResult[]>([]);
14
+
15
+ async ngOnInit() {
16
+ this.#listenerHandlers.push(
17
+ await BrotherPrint.addListener(BrotherPrintEventsEnum.onPrint, () => {
18
+ console.log('onPrint');
19
+ }),
20
+ );
21
+ this.#listenerHandlers.push(
22
+ await BrotherPrint.addListener(BrotherPrintEventsEnum.onPrintError, (info) => {
23
+ console.log('onPrintError');
24
+ }),
25
+ );
26
+ this.#listenerHandlers.push(
27
+ await BrotherPrint.addListener(BrotherPrintEventsEnum.onPrintFailedCommunication, (info) => {
28
+ console.log('onPrintFailedCommunication');
29
+ }),
30
+ );
31
+ this.#listenerHandlers.push(
32
+ await BrotherPrint.addListener(BrotherPrintEventsEnum.onPrinterAvailable, (printer) => {
33
+ this.printers.update((prev) => [...prev, printer]);
34
+ }),
35
+ );
36
+ }
37
+
38
+ async ngOnDestroy() {
39
+ this.#listenerHandlers.forEach((handler) => handler.remove());
40
+ }
41
+
42
+ async searchPrinter(port: BRKMPrinterPort) {
43
+ // This method return void. Get the printer list by listening to the event.
44
+ await BrotherPrint.search({
45
+ port,
46
+ searchDuration: 15, // seconds
47
+ });
48
+ }
49
+
50
+ print() {
51
+ if (this.printers().length === 0) {
52
+ console.error('No printer found');
53
+ return;
54
+ }
55
+
56
+ const defaultPrintSettings: BRLMPrintOptions = {
57
+ modelName: BRLMPrinterModelName.QL_820NWB,
58
+ labelName: BRLMPrinterLabelName.RollW62,
59
+ encodedImage: 'base64 removed mime-type', // base64
60
+ numberOfCopies: 1, // default 1
61
+ autoCut: true, // default true
62
+ };
63
+
64
+ BrotherPrint.printImage({
65
+ ...defaultPrintSettings,
66
+ ...{
67
+ port: this.printers()[0].port,
68
+ channelInfo: this.printers()[0].channelInfo,
69
+ },
70
+ });
71
+ }
72
+ }
73
+ ```
74
+
75
+ See demo for complete code:
76
+ https://github.com/rdlabo-dev/capacitor-brotherprint/blob/main/demo/src/app/home/home.page.ts
@@ -23,11 +23,16 @@ public class BrotherPrintPlugin: CAPPlugin, CAPBridgedPlugin {
23
23
  @objc func printImage(_ call: CAPPluginCall) {
24
24
  let encodedImage: String = call.getString("encodedImage", "")
25
25
  if encodedImage == "" {
26
+ self.notifyListeners(BrotherPrinterEvent.onPrintError.rawValue, data: ["code": 0, "message": "Error - Image data is not found."])
26
27
  call.reject("Error - Image data is not found.")
27
28
  return
28
29
  }
29
30
 
30
- let newImageData = Data(base64Encoded: encodedImage, options: [])
31
+ guard let image = decodePrintImage(encodedImage) else {
32
+ self.notifyListeners(BrotherPrinterEvent.onPrintError.rawValue, data: ["code": 0, "message": "Error - Create decodedByte From ImageData is failed."])
33
+ call.reject("Error - Create decodedByte From ImageData is failed.")
34
+ return
35
+ }
31
36
 
32
37
  // 検索からデバイス情報が得られた場合
33
38
  let port: String = call.getString("port", "wifi")
@@ -54,6 +59,7 @@ public class BrotherPrintPlugin: CAPPlugin, CAPBridgedPlugin {
54
59
  case "bluetoothLowEnergy":
55
60
  channel = BRLMChannel(bleLocalName: channelInfo)
56
61
  default:
62
+ self.notifyListeners(BrotherPrinterEvent.onPrintError.rawValue, data: ["code": 0, "message": "Error - connection is not found."])
57
63
  call.reject("Error - connection is not found.")
58
64
  return
59
65
  }
@@ -70,14 +76,6 @@ public class BrotherPrintPlugin: CAPPlugin, CAPBridgedPlugin {
70
76
  return
71
77
  }
72
78
 
73
- guard
74
- let decodedByte = UIImage(data: newImageData! as Data)
75
- else {
76
- printerDriver.closeChannel()
77
- call.reject("Error - Create decodedByte From ImageData is failed.")
78
- return
79
- }
80
-
81
79
  var printSettings: BRLMPrintSettingsProtocol
82
80
 
83
81
  if modelName.hasPrefix("QL") {
@@ -85,7 +83,7 @@ public class BrotherPrintPlugin: CAPPlugin, CAPBridgedPlugin {
85
83
  let _printSettings = BRLMQLPrintSettings(defaultPrintSettingsWith: printerModel)
86
84
  else {
87
85
  printerDriver.closeChannel()
88
- self.notifyListeners(BrotherPrinterEvent.onPrintFailedCommunication.rawValue, data: [
86
+ self.notifyListeners(BrotherPrinterEvent.onPrintError.rawValue, data: [
89
87
  "code": 0,
90
88
  "message": "Error - Create BRLMQLPrintSettings with " + modelName + " is failed."
91
89
  ])
@@ -99,7 +97,7 @@ public class BrotherPrintPlugin: CAPPlugin, CAPBridgedPlugin {
99
97
  let _printSettings = BRLMTDPrintSettings(defaultPrintSettingsWith: printerModel)
100
98
  else {
101
99
  printerDriver.closeChannel()
102
- self.notifyListeners(BrotherPrinterEvent.onPrintFailedCommunication.rawValue, data: [
100
+ self.notifyListeners(BrotherPrinterEvent.onPrintError.rawValue, data: [
103
101
  "code": 0,
104
102
  "message": "Error - Create BRLMTDPrintSettings with " + modelName + " is failed."
105
103
  ])
@@ -110,11 +108,12 @@ public class BrotherPrintPlugin: CAPPlugin, CAPBridgedPlugin {
110
108
 
111
109
  } else {
112
110
  printerDriver.closeChannel()
111
+ self.notifyListeners(BrotherPrinterEvent.onPrintError.rawValue, data: ["code": 0, "message": "Error - " + modelName + " is not supported"])
113
112
  call.reject("Error - " + modelName + " is not supported")
114
113
  return
115
114
  }
116
115
 
117
- let printError = printerDriver.printImage(with: decodedByte.cgImage!, settings: printSettings)
116
+ let printError = printerDriver.printImage(with: image, settings: printSettings)
118
117
 
119
118
  if printError.code != BRLMPrintErrorCode.noError {
120
119
  printerDriver.closeChannel()
@@ -209,6 +208,21 @@ public class BrotherPrintPlugin: CAPPlugin, CAPBridgedPlugin {
209
208
  call.reject("Error - startBluetoothSearch: " + PrinterSearchErrorModel.fetchChannelErrorCode(error: searcher.error.code))
210
209
  return
211
210
  }
211
+ if searcher.channels.isEmpty {
212
+ DispatchQueue.main.async {
213
+ BRLMPrinterSearcher.startBluetoothAccessorySearch { result in
214
+ guard result.error.code == BRLMPrinterSearchErrorCode.noError else {
215
+ call.reject("Error - startBluetoothAccessorySearch: " + PrinterSearchErrorModel.fetchChannelErrorCode(error: result.error.code))
216
+ return
217
+ }
218
+ for channel in result.channels {
219
+ self.notifyListeners(BrotherPrinterEvent.onPrinterAvailable.rawValue, data: self.chanelToPrinter(port: "bluetooth", channel: channel))
220
+ }
221
+ call.resolve()
222
+ }
223
+ }
224
+ return
225
+ }
212
226
  for channel in searcher.channels {
213
227
  NSLog(channel.channelInfo)
214
228
  self.notifyListeners(BrotherPrinterEvent.onPrinterAvailable.rawValue, data: self.chanelToPrinter(port: "bluetooth", channel: channel))
@@ -216,12 +230,6 @@ public class BrotherPrintPlugin: CAPPlugin, CAPBridgedPlugin {
216
230
  call.resolve()
217
231
  }
218
232
 
219
- // BRLMPrinterSearcher.startBluetoothAccessorySearch() { searcher in
220
- // for channel in searcher.channels {
221
- // self.notifyListeners(BrotherPrinterEvent.onPrinterAvailable.rawValue, data: self.chanelToPrinter(port: "bluetooth", channel: channel))
222
- // }
223
- // call.resolve()
224
- // }
225
233
  }
226
234
 
227
235
  private func searchBLEPrinter(_ call: CAPPluginCall) {
@@ -229,13 +237,17 @@ public class BrotherPrintPlugin: CAPPlugin, CAPBridgedPlugin {
229
237
  self.cancelRoutineBluetooth = {
230
238
  BRLMPrinterSearcher.cancelBLESearch()
231
239
  }
240
+ defer { self.cancelRoutineBluetooth = nil }
232
241
  let option = BRLMBLESearchOption()
233
242
  option.searchDuration = TimeInterval(call.getInt("searchDuration", 15))
234
243
  NSLog("BRLMPrinterSearcher.startBLESearch")
235
- BRLMPrinterSearcher.startBLESearch(option) { channel in
244
+ let searcher = BRLMPrinterSearcher.startBLESearch(option) { channel in
236
245
  self.notifyListeners(BrotherPrinterEvent.onPrinterAvailable.rawValue, data: self.chanelToPrinter(port: "bluetoothLowEnergy", channel: channel))
237
246
  }
238
- self.cancelRoutineBluetooth = nil
247
+ guard searcher.error.code == BRLMPrinterSearchErrorCode.noError else {
248
+ call.reject("Error - startBLESearch: " + PrinterSearchErrorModel.fetchChannelErrorCode(error: searcher.error.code))
249
+ return
250
+ }
239
251
  call.resolve()
240
252
  }
241
253
  }
@@ -263,6 +275,7 @@ public class BrotherPrintPlugin: CAPPlugin, CAPBridgedPlugin {
263
275
  DispatchQueue.global().async {
264
276
  self.cancelRoutineWiFi?()
265
277
  self.cancelRoutineWiFi = nil
278
+ call.resolve()
266
279
  }
267
280
  }
268
281
 
@@ -270,6 +283,7 @@ public class BrotherPrintPlugin: CAPPlugin, CAPBridgedPlugin {
270
283
  DispatchQueue.global().async {
271
284
  self.cancelRoutineBluetooth?()
272
285
  self.cancelRoutineBluetooth = nil
286
+ call.resolve()
273
287
  }
274
288
  }
275
289
  }
@@ -33,6 +33,8 @@
33
33
  <string>_ipp._tcp</string>
34
34
  </array>
35
35
  <key>UISupportedExternalAccessoryProtocols</key>
36
- <string>com.brother.ptcbp</string>
36
+ <array>
37
+ <string>com.brother.ptcbp</string>
38
+ </array>
37
39
  </dict>
38
40
  </plist>
@@ -0,0 +1,9 @@
1
+ import UIKit
2
+
3
+ func decodePrintImage(_ encodedImage: String) -> CGImage? {
4
+ guard let data = Data(base64Encoded: encodedImage, options: []),
5
+ let image = UIImage(data: data) else {
6
+ return nil
7
+ }
8
+ return image.cgImage
9
+ }
@@ -1,15 +1,36 @@
1
1
  import XCTest
2
+ import Capacitor
2
3
  @testable import BrotherPrintPlugin
3
4
 
4
- class ExampleTests: XCTestCase {
5
- func testEcho() {
6
- // This is an example of a functional test case for a plugin.
7
- // Use XCTAssert and related functions to verify your tests produce the correct results.
8
-
9
- let implementation = BrotherPrintPlugin()
10
- let value = "Hello, World!"
11
- let result = implementation.echo(value)
12
-
13
- XCTAssertEqual(value, result)
5
+ class PrintImageValidationTests: XCTestCase {
6
+ func testInvalidImagesRejectBeforeOpeningPrinter() {
7
+ for image in ["", "A", "SGVsbG8=", "iVBORw0KGgo="] {
8
+ var rejected = 0
9
+ var errorEvents = 0
10
+ var errorMessage: String?
11
+ let plugin = BrotherPrintPlugin()
12
+ // Normally initialized by Capacitor when attaching the bridge.
13
+ plugin.eventListeners = NSMutableDictionary()
14
+ for eventName in ["onPrintError", "onPrint", "onPrintFailedCommunication"] {
15
+ let listener = CAPPluginCall(callbackId: eventName, methodName: "addListener", options: ["eventName": eventName], success: { result, _ in
16
+ XCTAssertEqual(eventName, "onPrintError")
17
+ XCTAssertEqual(result?.data?["code"] as? Int, 0)
18
+ errorMessage = result?.data?["message"] as? String
19
+ errorEvents += 1
20
+ }, error: { _ in
21
+ XCTFail("Listener must not reject")
22
+ })!
23
+ plugin.addListener(listener)
24
+ }
25
+ let call = CAPPluginCall(callbackId: "test", methodName: "printImage", options: ["encodedImage": image], success: { _, _ in
26
+ XCTFail("Invalid image must not resolve")
27
+ }, error: { error in
28
+ XCTAssertEqual(errorMessage, error?.message)
29
+ rejected += 1
30
+ })!
31
+ plugin.printImage(call)
32
+ XCTAssertEqual(rejected, 1, "Input: \(image)")
33
+ XCTAssertEqual(errorEvents, 1, "Input: \(image)")
34
+ }
14
35
  }
15
36
  }
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@rdlabo/capacitor-brotherprint",
3
- "version": "8.1.1",
3
+ "private": false,
4
+ "version": "8.2.0",
4
5
  "description": "Capacitor plugin for Brother Print SDK",
5
6
  "main": "dist/plugin.cjs.js",
6
7
  "module": "dist/esm/index.js",
@@ -68,6 +69,7 @@
68
69
  "android/src/main/",
69
70
  "android/build.gradle",
70
71
  "dist/",
72
+ "docs/",
71
73
  "ios/Sources",
72
74
  "ios/Tests",
73
75
  "Package.swift",
@@ -93,9 +95,10 @@
93
95
  },
94
96
  "repository": {
95
97
  "type": "git",
96
- "url": "git@github.com:rdlabo-team/capacitor-brotherprint.git"
98
+ "url": "git@github.com:rdlabo-dev/capacitor-brotherprint.git"
97
99
  },
98
100
  "bugs": {
99
- "url": "git@github.com:rdlabo-team/capacitor-brotherprint.git/issues"
100
- }
101
+ "url": "git@github.com:rdlabo-dev/capacitor-brotherprint.git/issues"
102
+ },
103
+ "homepage": "https://docs.rdlabo.dev/projects/capacitor-brotherprint"
101
104
  }