@dimer47/capacitor-plugin-printer 2.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/CapacitorPluginPrinter.podspec +17 -0
- package/LICENSE +202 -0
- package/Package.swift +31 -0
- package/README.md +505 -0
- package/android/build.gradle +68 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/kotlin/com/nichedev/capacitor/printer/PrintAdapter.kt +73 -0
- package/android/src/main/kotlin/com/nichedev/capacitor/printer/PrintContent.kt +78 -0
- package/android/src/main/kotlin/com/nichedev/capacitor/printer/PrintIO.kt +136 -0
- package/android/src/main/kotlin/com/nichedev/capacitor/printer/PrintManager.kt +219 -0
- package/android/src/main/kotlin/com/nichedev/capacitor/printer/PrintOptions.kt +260 -0
- package/android/src/main/kotlin/com/nichedev/capacitor/printer/PrintProxy.kt +44 -0
- package/android/src/main/kotlin/com/nichedev/capacitor/printer/PrinterPlugin.kt +267 -0
- package/dist/esm/definitions.d.ts +307 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +13 -0
- package/dist/esm/web.js +47 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +61 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +64 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/PrinterPlugin/PrinterControllerHelper.swift +33 -0
- package/ios/Sources/PrinterPlugin/PrinterFont.swift +99 -0
- package/ios/Sources/PrinterPlugin/PrinterInfo.swift +64 -0
- package/ios/Sources/PrinterPlugin/PrinterItem.swift +104 -0
- package/ios/Sources/PrinterPlugin/PrinterLayout.swift +61 -0
- package/ios/Sources/PrinterPlugin/PrinterPaper.swift +71 -0
- package/ios/Sources/PrinterPlugin/PrinterPlugin.swift +327 -0
- package/ios/Sources/PrinterPlugin/PrinterRenderer.swift +135 -0
- package/ios/Sources/PrinterPlugin/PrinterUnit.swift +45 -0
- package/package.json +75 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Capacitor
|
|
3
|
+
import UIKit
|
|
4
|
+
|
|
5
|
+
@objc(PrinterPlugin)
|
|
6
|
+
public class PrinterPlugin: CAPPlugin, CAPBridgedPlugin, UIPrintInteractionControllerDelegate {
|
|
7
|
+
public let identifier = "PrinterPlugin"
|
|
8
|
+
public let jsName = "Printer"
|
|
9
|
+
public let pluginMethods: [CAPPluginMethod] = [
|
|
10
|
+
CAPPluginMethod(name: "print", returnType: CAPPluginReturnPromise),
|
|
11
|
+
CAPPluginMethod(name: "printHtml", returnType: CAPPluginReturnPromise),
|
|
12
|
+
CAPPluginMethod(name: "printPdf", returnType: CAPPluginReturnPromise),
|
|
13
|
+
CAPPluginMethod(name: "printBase64", returnType: CAPPluginReturnPromise),
|
|
14
|
+
CAPPluginMethod(name: "printFile", returnType: CAPPluginReturnPromise),
|
|
15
|
+
CAPPluginMethod(name: "printWebView", returnType: CAPPluginReturnPromise),
|
|
16
|
+
CAPPluginMethod(name: "canPrintItem", returnType: CAPPluginReturnPromise),
|
|
17
|
+
CAPPluginMethod(name: "getPrintableTypes", returnType: CAPPluginReturnPromise),
|
|
18
|
+
CAPPluginMethod(name: "pick", returnType: CAPPluginReturnPromise),
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
private var previousPrinter: UIPrinter?
|
|
22
|
+
private var currentSettings: [String: Any] = [:]
|
|
23
|
+
|
|
24
|
+
// MARK: - Plugin Methods
|
|
25
|
+
|
|
26
|
+
@objc func canPrintItem(_ call: CAPPluginCall) {
|
|
27
|
+
let uri = call.getString("uri")
|
|
28
|
+
|
|
29
|
+
DispatchQueue.main.async {
|
|
30
|
+
let available = PrinterItem.canPrintURL(uri)
|
|
31
|
+
call.resolve(["available": available])
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@objc func getPrintableTypes(_ call: CAPPluginCall) {
|
|
36
|
+
DispatchQueue.main.async {
|
|
37
|
+
let utis = UIPrintInteractionController.printableUTIs
|
|
38
|
+
let typesArray = utis.map { $0 as String }
|
|
39
|
+
call.resolve(["types": typesArray])
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@objc func pick(_ call: CAPPluginCall) {
|
|
44
|
+
let uiOptions = call.getObject("ui") ?? [:]
|
|
45
|
+
|
|
46
|
+
DispatchQueue.main.async {
|
|
47
|
+
let controller = UIPrinterPickerController(initiallySelectedPrinter: nil)
|
|
48
|
+
|
|
49
|
+
let handler: UIPrinterPickerController.CompletionHandler = { [weak self] pickerController, selected, error in
|
|
50
|
+
guard let self = self else { return }
|
|
51
|
+
|
|
52
|
+
if let printer = pickerController.selectedPrinter {
|
|
53
|
+
self.rememberPrinter(printer)
|
|
54
|
+
call.resolve(["url": printer.url.absoluteString])
|
|
55
|
+
} else {
|
|
56
|
+
call.resolve([:])
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if UIDevice.current.userInterfaceIdiom == .pad {
|
|
61
|
+
let rect = self.rectFromDictionary(uiOptions)
|
|
62
|
+
if let webView = self.bridge?.webView {
|
|
63
|
+
controller.present(from: rect, in: webView, animated: true, completionHandler: handler)
|
|
64
|
+
} else {
|
|
65
|
+
controller.present(animated: true, completionHandler: handler)
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
controller.present(animated: true, completionHandler: handler)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@objc func print(_ call: CAPPluginCall) {
|
|
74
|
+
let content = call.getString("content")
|
|
75
|
+
let settings = self.extractSettings(from: call)
|
|
76
|
+
|
|
77
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
78
|
+
self.printContent(content, withSettings: settings, call: call)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@objc func printHtml(_ call: CAPPluginCall) {
|
|
83
|
+
guard let html = call.getString("html") else {
|
|
84
|
+
call.reject("The 'html' parameter is required")
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
let settings = self.extractSettings(from: call)
|
|
88
|
+
|
|
89
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
90
|
+
self.printContent(html, withSettings: settings, call: call)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@objc func printPdf(_ call: CAPPluginCall) {
|
|
95
|
+
guard let path = call.getString("path") else {
|
|
96
|
+
call.reject("The 'path' parameter is required")
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
let settings = self.extractSettings(from: call)
|
|
100
|
+
|
|
101
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
102
|
+
self.printContent(path, withSettings: settings, call: call)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
@objc func printBase64(_ call: CAPPluginCall) {
|
|
107
|
+
guard let data = call.getString("data") else {
|
|
108
|
+
call.reject("The 'data' parameter is required")
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
guard call.getString("mimeType") != nil else {
|
|
112
|
+
call.reject("The 'mimeType' parameter is required")
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
let content = "base64:" + data
|
|
116
|
+
let settings = self.extractSettings(from: call)
|
|
117
|
+
|
|
118
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
119
|
+
self.printContent(content, withSettings: settings, call: call)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@objc func printFile(_ call: CAPPluginCall) {
|
|
124
|
+
guard let path = call.getString("path") else {
|
|
125
|
+
call.reject("The 'path' parameter is required")
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
let settings = self.extractSettings(from: call)
|
|
129
|
+
|
|
130
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
131
|
+
self.printContent(path, withSettings: settings, call: call)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@objc func printWebView(_ call: CAPPluginCall) {
|
|
136
|
+
let settings = self.extractSettings(from: call)
|
|
137
|
+
|
|
138
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
139
|
+
self.printContent(nil, withSettings: settings, call: call)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// MARK: - UIPrintInteractionControllerDelegate
|
|
144
|
+
|
|
145
|
+
public func printInteractionController(
|
|
146
|
+
_ printInteractionController: UIPrintInteractionController,
|
|
147
|
+
choosePaper paperList: [UIPrintPaper]
|
|
148
|
+
) -> UIPrintPaper {
|
|
149
|
+
if let paperSpec = currentSettings["paper"] as? [String: Any] {
|
|
150
|
+
let paper = PrinterPaper(dictionary: paperSpec)
|
|
151
|
+
if let bestPaper = paper.bestPaper(from: paperList) {
|
|
152
|
+
return bestPaper
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return paperList.first ?? UIPrintPaper.bestPaper(forPageSize: CGSize(width: 612, height: 792), withPapersFrom: paperList)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
public func printInteractionController(
|
|
159
|
+
_ printInteractionController: UIPrintInteractionController,
|
|
160
|
+
cutLengthFor paper: UIPrintPaper
|
|
161
|
+
) -> CGFloat {
|
|
162
|
+
if let paperSpec = currentSettings["paper"] as? [String: Any] {
|
|
163
|
+
let paperObj = PrinterPaper(dictionary: paperSpec)
|
|
164
|
+
if paperObj.length > 0 {
|
|
165
|
+
return paperObj.length
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return paper.paperSize.height
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// MARK: - Core Printing
|
|
172
|
+
|
|
173
|
+
private func printContent(_ content: String?, withSettings settings: [String: Any], call: CAPPluginCall) {
|
|
174
|
+
var item: Any?
|
|
175
|
+
var ctrl: UIPrintInteractionController!
|
|
176
|
+
|
|
177
|
+
DispatchQueue.main.sync {
|
|
178
|
+
ctrl = PrinterControllerHelper.sharedController(with: settings)
|
|
179
|
+
ctrl.delegate = self
|
|
180
|
+
self.currentSettings = settings
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if content == nil || content?.isEmpty == true {
|
|
184
|
+
DispatchQueue.main.sync {
|
|
185
|
+
if let webView = self.bridge?.webView {
|
|
186
|
+
item = webView.viewPrintFormatter()
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} else if let content = content, content.hasPrefix("<") {
|
|
190
|
+
DispatchQueue.main.sync {
|
|
191
|
+
item = UIMarkupTextPrintFormatter(markupText: content)
|
|
192
|
+
}
|
|
193
|
+
} else if let content = content, URL(string: content)?.scheme != nil {
|
|
194
|
+
item = PrinterItem.item(from: content)
|
|
195
|
+
} else if let content = content {
|
|
196
|
+
DispatchQueue.main.sync {
|
|
197
|
+
item = UISimpleTextPrintFormatter(text: content)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
self.useController(ctrl, toPrintItem: item, withSettings: settings, call: call)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private func useController(
|
|
205
|
+
_ ctrl: UIPrintInteractionController,
|
|
206
|
+
toPrintItem item: Any?,
|
|
207
|
+
withSettings settings: [String: Any],
|
|
208
|
+
call: CAPPluginCall
|
|
209
|
+
) {
|
|
210
|
+
let printerURL = settings["printer"] as? String
|
|
211
|
+
|
|
212
|
+
if let formatter = item as? UIPrintFormatter {
|
|
213
|
+
ctrl.printPageRenderer = PrinterRenderer(dictionary: settings, formatter: formatter)
|
|
214
|
+
} else {
|
|
215
|
+
ctrl.printingItem = item
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if let printerURL = printerURL, !printerURL.isEmpty {
|
|
219
|
+
self.printToPrinter(ctrl, withSettings: settings, call: call)
|
|
220
|
+
} else {
|
|
221
|
+
self.presentController(ctrl, withSettings: settings, call: call)
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
private func printToPrinter(
|
|
226
|
+
_ ctrl: UIPrintInteractionController,
|
|
227
|
+
withSettings settings: [String: Any],
|
|
228
|
+
call: CAPPluginCall
|
|
229
|
+
) {
|
|
230
|
+
let printerURLString = settings["printer"] as? String ?? ""
|
|
231
|
+
guard let printer = self.printer(withURL: printerURLString) else {
|
|
232
|
+
self.presentController(ctrl, withSettings: settings, call: call)
|
|
233
|
+
return
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
DispatchQueue.main.async {
|
|
237
|
+
ctrl.print(to: printer) { [weak self] _, completed, _ in
|
|
238
|
+
self?.rememberPrinter(completed ? printer : nil)
|
|
239
|
+
call.resolve(["success": completed])
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private func presentController(
|
|
245
|
+
_ ctrl: UIPrintInteractionController,
|
|
246
|
+
withSettings settings: [String: Any],
|
|
247
|
+
call: CAPPluginCall
|
|
248
|
+
) {
|
|
249
|
+
let uiOptions = settings["ui"] as? [String: Any] ?? [:]
|
|
250
|
+
let rect = self.rectFromDictionary(uiOptions)
|
|
251
|
+
|
|
252
|
+
let handler: UIPrintInteractionController.CompletionHandler = { _, completed, _ in
|
|
253
|
+
call.resolve(["success": completed])
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
DispatchQueue.main.async {
|
|
257
|
+
if UIDevice.current.userInterfaceIdiom == .pad {
|
|
258
|
+
if let webView = self.bridge?.webView {
|
|
259
|
+
ctrl.present(from: rect, in: webView, animated: true, completionHandler: handler)
|
|
260
|
+
} else {
|
|
261
|
+
ctrl.present(animated: true, completionHandler: handler)
|
|
262
|
+
}
|
|
263
|
+
} else {
|
|
264
|
+
ctrl.present(animated: true, completionHandler: handler)
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// MARK: - Helpers
|
|
270
|
+
|
|
271
|
+
private func extractSettings(from call: CAPPluginCall) -> [String: Any] {
|
|
272
|
+
var settings: [String: Any] = [:]
|
|
273
|
+
|
|
274
|
+
if let name = call.getString("name") { settings["name"] = name }
|
|
275
|
+
if let orientation = call.getString("orientation") { settings["orientation"] = orientation }
|
|
276
|
+
if let duplex = call.getString("duplex") { settings["duplex"] = duplex }
|
|
277
|
+
if let printer = call.getString("printer") { settings["printer"] = printer }
|
|
278
|
+
if let maxWidth = call.getString("maxWidth") ?? (call.getInt("maxWidth").map { String($0) }) { settings["maxWidth"] = maxWidth }
|
|
279
|
+
if let maxHeight = call.getString("maxHeight") ?? (call.getInt("maxHeight").map { String($0) }) { settings["maxHeight"] = maxHeight }
|
|
280
|
+
|
|
281
|
+
settings["monochrome"] = call.getBool("monochrome") ?? false
|
|
282
|
+
settings["photo"] = call.getBool("photo") ?? false
|
|
283
|
+
settings["copies"] = call.getInt("copies") ?? 1
|
|
284
|
+
|
|
285
|
+
if let pageCount = call.getInt("pageCount") { settings["pageCount"] = pageCount }
|
|
286
|
+
|
|
287
|
+
if let margin = call.getObject("margin") {
|
|
288
|
+
settings["margin"] = margin
|
|
289
|
+
} else if let marginBool = call.getBool("margin") {
|
|
290
|
+
if !marginBool {
|
|
291
|
+
settings["margin"] = false
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if let font = call.getObject("font") { settings["font"] = font }
|
|
296
|
+
if let header = call.getObject("header") { settings["header"] = header }
|
|
297
|
+
if let footer = call.getObject("footer") { settings["footer"] = footer }
|
|
298
|
+
if let paper = call.getObject("paper") { settings["paper"] = paper }
|
|
299
|
+
if let ui = call.getObject("ui") { settings["ui"] = ui }
|
|
300
|
+
|
|
301
|
+
return settings
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
private func rememberPrinter(_ printer: UIPrinter?) {
|
|
305
|
+
previousPrinter = printer
|
|
306
|
+
if let printer = printer {
|
|
307
|
+
_ = UIPrinterPickerController(initiallySelectedPrinter: printer)
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
private func printer(withURL urlString: String) -> UIPrinter? {
|
|
312
|
+
if let prev = previousPrinter, prev.url.absoluteString == urlString {
|
|
313
|
+
return prev
|
|
314
|
+
}
|
|
315
|
+
guard let url = URL(string: urlString) else { return nil }
|
|
316
|
+
return UIPrinter(url: url)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
private func rectFromDictionary(_ pos: [String: Any]) -> CGRect {
|
|
320
|
+
let left = (pos["left"] as? NSNumber)?.doubleValue ?? 40.0
|
|
321
|
+
let top = (pos["top"] as? NSNumber)?.doubleValue ?? 30.0
|
|
322
|
+
let width = (pos["width"] as? NSNumber)?.doubleValue ?? 0.0
|
|
323
|
+
let height = (pos["height"] as? NSNumber)?.doubleValue ?? 0.0
|
|
324
|
+
|
|
325
|
+
return CGRect(x: left, y: top, width: width, height: height)
|
|
326
|
+
}
|
|
327
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
/// Custom page renderer supporting headers, footers, and page count control.
|
|
5
|
+
class PrinterRenderer: UIPrintPageRenderer {
|
|
6
|
+
|
|
7
|
+
private let settings: [String: Any]
|
|
8
|
+
|
|
9
|
+
init(dictionary spec: [String: Any], formatter: UIPrintFormatter) {
|
|
10
|
+
self.settings = spec
|
|
11
|
+
|
|
12
|
+
super.init()
|
|
13
|
+
|
|
14
|
+
let _ = PrinterLayout.configureFormatter(formatter, withSettings: spec)
|
|
15
|
+
self.addPrintFormatter(formatter, startingAtPageAt: 0)
|
|
16
|
+
|
|
17
|
+
if let header = spec["header"] as? [String: Any] {
|
|
18
|
+
self.headerHeight = CGFloat(PrinterUnit.convert(header["height"]))
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if let footer = spec["footer"] as? [String: Any] {
|
|
22
|
+
self.footerHeight = CGFloat(PrinterUnit.convert(footer["height"]))
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
override var numberOfPages: Int {
|
|
27
|
+
let num = super.numberOfPages
|
|
28
|
+
let maxPages = settings["pageCount"]
|
|
29
|
+
|
|
30
|
+
if let maxPagesInt = maxPages as? Int {
|
|
31
|
+
if maxPagesInt < 0 {
|
|
32
|
+
return max(1, num + maxPagesInt)
|
|
33
|
+
}
|
|
34
|
+
return maxPagesInt > 0 ? max(1, min(num, maxPagesInt)) : num
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return num
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
override func drawHeaderForPage(at pageIndex: Int, in headerRect: CGRect) {
|
|
41
|
+
if let header = settings["header"] as? [String: Any] {
|
|
42
|
+
drawLabels(from: header, forPageAt: pageIndex, in: headerRect)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
override func drawFooterForPage(at pageIndex: Int, in footerRect: CGRect) {
|
|
47
|
+
if let footer = settings["footer"] as? [String: Any] {
|
|
48
|
+
drawLabels(from: footer, forPageAt: pageIndex, in: footerRect)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// MARK: - Private
|
|
53
|
+
|
|
54
|
+
private func drawLabels(from spec: [String: Any], forPageAt index: Int, in rect: CGRect) {
|
|
55
|
+
var labels: [[String: Any]]?
|
|
56
|
+
|
|
57
|
+
if let label = spec["label"] as? [String: Any] {
|
|
58
|
+
labels = [label]
|
|
59
|
+
} else if let specLabels = spec["labels"] as? [[String: Any]] {
|
|
60
|
+
labels = specLabels
|
|
61
|
+
} else if let text = spec["text"] as? String, !text.isEmpty {
|
|
62
|
+
labels = [["text": text]]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
guard let labelsList = labels else { return }
|
|
66
|
+
|
|
67
|
+
for label in labelsList {
|
|
68
|
+
drawLabel(from: label, forPageAt: index, in: rect)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private func drawLabel(from spec: [String: Any], forPageAt index: Int, in rect: CGRect) {
|
|
73
|
+
var label = spec["text"] as? String
|
|
74
|
+
let showIndex = spec["showPageIndex"] as? Bool ?? false
|
|
75
|
+
|
|
76
|
+
if showIndex {
|
|
77
|
+
let format: String
|
|
78
|
+
if let existingLabel = label, !existingLabel.isEmpty {
|
|
79
|
+
format = existingLabel
|
|
80
|
+
} else {
|
|
81
|
+
format = "%ld"
|
|
82
|
+
}
|
|
83
|
+
label = String(format: format, index + 1)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
guard let labelText = label, !labelText.isEmpty else { return }
|
|
87
|
+
|
|
88
|
+
let font = PrinterFont(dictionary: spec["font"] as? [String: Any])
|
|
89
|
+
let attributes = font.attributes
|
|
90
|
+
|
|
91
|
+
let hasPosition =
|
|
92
|
+
spec["top"] != nil || spec["left"] != nil || spec["right"] != nil || spec["bottom"] != nil
|
|
93
|
+
|
|
94
|
+
if hasPosition {
|
|
95
|
+
let point = pointFromPosition(spec, forLabel: labelText, withAttributes: attributes, in: rect)
|
|
96
|
+
labelText.draw(at: point, withAttributes: attributes)
|
|
97
|
+
} else {
|
|
98
|
+
labelText.draw(in: rect, withAttributes: attributes)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private func pointFromPosition(
|
|
103
|
+
_ spec: [String: Any],
|
|
104
|
+
forLabel label: String,
|
|
105
|
+
withAttributes attributes: [NSAttributedString.Key: Any],
|
|
106
|
+
in rect: CGRect
|
|
107
|
+
) -> CGPoint {
|
|
108
|
+
let top = spec["top"]
|
|
109
|
+
let left = spec["left"]
|
|
110
|
+
let right = spec["right"]
|
|
111
|
+
let bottom = spec["bottom"]
|
|
112
|
+
|
|
113
|
+
var x = rect.origin.x
|
|
114
|
+
var y = rect.origin.y
|
|
115
|
+
|
|
116
|
+
var size: CGSize = .zero
|
|
117
|
+
if bottom != nil || right != nil {
|
|
118
|
+
size = label.size(withAttributes: attributes)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if let top = top {
|
|
122
|
+
y = rect.origin.y + CGFloat(PrinterUnit.convert(top))
|
|
123
|
+
} else if let bottom = bottom {
|
|
124
|
+
y = rect.origin.y + rect.size.height - size.height - CGFloat(PrinterUnit.convert(bottom))
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if let left = left {
|
|
128
|
+
x = rect.origin.x + CGFloat(PrinterUnit.convert(left))
|
|
129
|
+
} else if let right = right {
|
|
130
|
+
x = rect.origin.x + rect.size.width - size.width - CGFloat(PrinterUnit.convert(right))
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return CGPoint(x: x, y: y)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// Converts various unit values (in, mm, cm, pt, numbers) to iOS points.
|
|
4
|
+
class PrinterUnit {
|
|
5
|
+
|
|
6
|
+
/// Converts any unit value to points.
|
|
7
|
+
/// Supports: numbers, "Xpt", "Xin", "Xmm", "Xcm", or numeric strings.
|
|
8
|
+
static func convert(_ unit: Any?) -> Double {
|
|
9
|
+
guard let unit = unit, !(unit is NSNull) else {
|
|
10
|
+
return 0
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if let number = unit as? NSNumber {
|
|
14
|
+
return number.doubleValue
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if let number = unit as? Int {
|
|
18
|
+
return Double(number)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if let number = unit as? Double {
|
|
22
|
+
return number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
guard let str = unit as? String, !str.isEmpty else {
|
|
26
|
+
return 0
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if str.hasSuffix("pt") {
|
|
30
|
+
let value = String(str.dropLast(2))
|
|
31
|
+
return Double(value) ?? 0
|
|
32
|
+
} else if str.hasSuffix("in") {
|
|
33
|
+
let value = String(str.dropLast(2))
|
|
34
|
+
return (Double(value) ?? 0) * 72.0
|
|
35
|
+
} else if str.hasSuffix("mm") {
|
|
36
|
+
let value = String(str.dropLast(2))
|
|
37
|
+
return (Double(value) ?? 0) * 72.0 / 25.4
|
|
38
|
+
} else if str.hasSuffix("cm") {
|
|
39
|
+
let value = String(str.dropLast(2))
|
|
40
|
+
return (Double(value) ?? 0) * 72.0 / 2.54
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return Double(str) ?? 0
|
|
44
|
+
}
|
|
45
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dimer47/capacitor-plugin-printer",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Capacitor plugin for printing HTML, PDF, images and plain text on iOS and Android",
|
|
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
|
+
"CapacitorPluginPrinter.podspec",
|
|
15
|
+
"Package.swift"
|
|
16
|
+
],
|
|
17
|
+
"keywords": [
|
|
18
|
+
"capacitor",
|
|
19
|
+
"plugin",
|
|
20
|
+
"native",
|
|
21
|
+
"printer",
|
|
22
|
+
"print",
|
|
23
|
+
"pdf",
|
|
24
|
+
"html",
|
|
25
|
+
"ios",
|
|
26
|
+
"android"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
|
|
30
|
+
"verify:ios": "cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin -destination generic/platform=iOS && cd ..",
|
|
31
|
+
"verify:android": "cd android && ./gradlew clean build test && cd ..",
|
|
32
|
+
"verify:web": "npm run build",
|
|
33
|
+
"lint": "npm run eslint && npm run prettier -- --check",
|
|
34
|
+
"fmt": "npm run eslint -- --fix && npm run prettier -- --write",
|
|
35
|
+
"eslint": "eslint . --ext ts",
|
|
36
|
+
"prettier": "prettier \"**/*.{css,html,ts,js,java}\"",
|
|
37
|
+
"build": "tsc && rollup -c rollup.config.mjs",
|
|
38
|
+
"test": "npm run test:ios && npm run test:android",
|
|
39
|
+
"test:ios": "xcodebuild test -scheme CapacitorPluginPrinter -destination 'platform=iOS Simulator,name=iPhone 17 Pro' -quiet",
|
|
40
|
+
"test:android": "cd \"${ANDROID_HOST_PROJECT:-../../../Solutions/Agri+/Codes/app-signing/code/android}\" && ./gradlew :capacitor-plugin-printer:testDebugUnitTest",
|
|
41
|
+
"prepublishOnly": "npm run build"
|
|
42
|
+
},
|
|
43
|
+
"author": "Dimer47",
|
|
44
|
+
"license": "Apache-2.0",
|
|
45
|
+
"capacitor": {
|
|
46
|
+
"ios": {
|
|
47
|
+
"src": "ios"
|
|
48
|
+
},
|
|
49
|
+
"android": {
|
|
50
|
+
"src": "android"
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"registry": "https://registry.npmjs.org",
|
|
55
|
+
"access": "public"
|
|
56
|
+
},
|
|
57
|
+
"repository": {
|
|
58
|
+
"type": "git",
|
|
59
|
+
"url": "git+https://github.com/dimer47/capacitor-plugin-printer.git"
|
|
60
|
+
},
|
|
61
|
+
"bugs": {
|
|
62
|
+
"url": "https://github.com/dimer47/capacitor-plugin-printer/issues"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@capacitor/android": "^8.0.0",
|
|
66
|
+
"@capacitor/core": "^8.0.0",
|
|
67
|
+
"@capacitor/ios": "^8.0.0",
|
|
68
|
+
"typescript": "~5.6.0",
|
|
69
|
+
"@rollup/plugin-node-resolve": "^16.0.0",
|
|
70
|
+
"rollup": "^4.0.0"
|
|
71
|
+
},
|
|
72
|
+
"peerDependencies": {
|
|
73
|
+
"@capacitor/core": ">=8.0.0"
|
|
74
|
+
}
|
|
75
|
+
}
|