@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.
Files changed (35) hide show
  1. package/BricksSoftCapacitorThermalPrinter.podspec +17 -0
  2. package/LICENSE +201 -0
  3. package/Package.swift +13 -0
  4. package/README.md +80 -2
  5. package/android/build.gradle +40 -0
  6. package/android/src/main/AndroidManifest.xml +8 -0
  7. package/android/src/main/java/com/brickssoft/thermalprinter/ThermalPrinterPlugin.java +463 -0
  8. package/dist/esm/definitions.d.ts +73 -0
  9. package/dist/esm/definitions.js +1 -0
  10. package/dist/esm/index.d.ts +3 -0
  11. package/dist/esm/index.js +23 -0
  12. package/dist/esm/web.d.ts +13 -0
  13. package/dist/esm/web.js +33 -0
  14. package/dist/plugin.cjs.js +65 -0
  15. package/dist/plugin.js +68 -0
  16. package/ios/Sources/ThermalPrinter/EscPosBarcode.swift +121 -0
  17. package/ios/Sources/ThermalPrinter/EscPosBitmap.swift +178 -0
  18. package/ios/Sources/ThermalPrinter/EscPosBluetooth.swift +341 -0
  19. package/ios/Sources/ThermalPrinter/EscPosBluetoothConnection.swift +298 -0
  20. package/ios/Sources/ThermalPrinter/EscPosBluetoothRules.swift +78 -0
  21. package/ios/Sources/ThermalPrinter/EscPosCharsetEncoding.swift +115 -0
  22. package/ios/Sources/ThermalPrinter/EscPosCoreBluetoothLink.swift +287 -0
  23. package/ios/Sources/ThermalPrinter/EscPosDeviceConnection.swift +47 -0
  24. package/ios/Sources/ThermalPrinter/EscPosJavaCompat.swift +213 -0
  25. package/ios/Sources/ThermalPrinter/EscPosKnownPrinterStore.swift +44 -0
  26. package/ios/Sources/ThermalPrinter/EscPosPrinter.swift +55 -0
  27. package/ios/Sources/ThermalPrinter/EscPosPrinterCommands.swift +367 -0
  28. package/ios/Sources/ThermalPrinter/EscPosPrinterSize.swift +53 -0
  29. package/ios/Sources/ThermalPrinter/EscPosQRCode.swift +70 -0
  30. package/ios/Sources/ThermalPrinter/EscPosQueue.swift +30 -0
  31. package/ios/Sources/ThermalPrinter/EscPosTcpConnection.swift +265 -0
  32. package/ios/Sources/ThermalPrinter/PrinterTextParser.swift +622 -0
  33. package/ios/Sources/ThermalPrinter/ThermalPrinterPlugin.swift +85 -0
  34. package/ios/Sources/ThermalPrinter/ThermalPrinterService.swift +380 -0
  35. package/package.json +61 -4
@@ -0,0 +1,622 @@
1
+ import Foundation
2
+
3
+ protocol PrinterTextParserElement: AnyObject {
4
+ func length() throws -> Int
5
+ func print(_ printerSocket: EscPosPrinterCommands) throws
6
+ }
7
+
8
+ final class PrinterTextParser {
9
+ static let tagsAlignLeft = "L"
10
+ static let tagsAlignCenter = "C"
11
+ static let tagsAlignRight = "R"
12
+ static let tagsAlign = [tagsAlignLeft, tagsAlignCenter, tagsAlignRight]
13
+
14
+ static let tagsImage = "img"
15
+ static let tagsBarcode = "barcode"
16
+ static let tagsQRCode = "qrcode"
17
+
18
+ static let attrBarcodeWidth = "width"
19
+ static let attrBarcodeHeight = "height"
20
+ static let attrBarcodeType = "type"
21
+ static let attrBarcodeTypeEAN13 = "ean13"
22
+ static let attrBarcodeTextPosition = "text"
23
+ static let attrBarcodeTextPositionNone = "none"
24
+ static let attrBarcodeTextPositionAbove = "above"
25
+
26
+ static let tagsFormatTextFont = "font"
27
+ static let tagsFormatTextBold = "b"
28
+ static let tagsFormatTextUnderline = "u"
29
+ static let tagsFormatText = [tagsFormatTextFont, tagsFormatTextBold, tagsFormatTextUnderline]
30
+
31
+ static let attrFormatTextUnderlineType = "type"
32
+ static let attrFormatTextUnderlineTypeNormal = "normal"
33
+ static let attrFormatTextUnderlineTypeDouble = "double"
34
+
35
+ static let attrFormatTextFontSize = "size"
36
+ static let attrFormatTextFontColor = "color"
37
+
38
+ static let attrQRCodeSize = "size"
39
+
40
+ static func isTagTextFormat(_ name: JavaString) throws -> Bool {
41
+ var tagName = name
42
+ if try tagName.substring(0, 1) == "/" {
43
+ tagName = try tagName.substring(1)
44
+ }
45
+ return tagsFormatText.contains { tagName == $0 }
46
+ }
47
+
48
+ let printer: EscPosPrinter
49
+ private var textSize: [[UInt8]] = [EscPosPrinterCommands.textSizeNormal]
50
+ private var textColor: [[UInt8]] = [EscPosPrinterCommands.textColorBlack]
51
+ private var textReverseColor: [[UInt8]] = [EscPosPrinterCommands.textColorReverseOff]
52
+ private var textBold: [[UInt8]] = [EscPosPrinterCommands.textWeightNormal]
53
+ private var textUnderline: [[UInt8]] = [EscPosPrinterCommands.textUnderlineOff]
54
+ private var textDoubleStrike: [[UInt8]] = [EscPosPrinterCommands.textDoubleStrikeOff]
55
+ private var text = JavaString("")
56
+
57
+ init(printer: EscPosPrinter) {
58
+ self.printer = printer
59
+ }
60
+
61
+ @discardableResult
62
+ func setFormattedText(_ text: JavaString) -> PrinterTextParser {
63
+ self.text = text
64
+ return self
65
+ }
66
+
67
+ var lastTextSize: [UInt8] { textSize[textSize.count - 1] }
68
+ var lastTextColor: [UInt8] { textColor[textColor.count - 1] }
69
+ var lastTextReverseColor: [UInt8] { textReverseColor[textReverseColor.count - 1] }
70
+ var lastTextBold: [UInt8] { textBold[textBold.count - 1] }
71
+ var lastTextUnderline: [UInt8] { textUnderline[textUnderline.count - 1] }
72
+ var lastTextDoubleStrike: [UInt8] { textDoubleStrike[textDoubleStrike.count - 1] }
73
+
74
+ func addTextSize(_ value: [UInt8]) { textSize.append(value) }
75
+ func addTextColor(_ value: [UInt8]) { textColor.append(value) }
76
+ func addTextReverseColor(_ value: [UInt8]) { textReverseColor.append(value) }
77
+ func addTextBold(_ value: [UInt8]) { textBold.append(value) }
78
+ func addTextUnderline(_ value: [UInt8]) { textUnderline.append(value) }
79
+ func addTextDoubleStrike(_ value: [UInt8]) { textDoubleStrike.append(value) }
80
+
81
+ func dropLastTextSize() { if textSize.count > 1 { textSize.removeLast() } }
82
+ func dropLastTextColor() { if textColor.count > 1 { textColor.removeLast() } }
83
+ func dropLastTextReverseColor() { if textReverseColor.count > 1 { textReverseColor.removeLast() } }
84
+ func dropTextBold() { if textBold.count > 1 { textBold.removeLast() } }
85
+ func dropLastTextUnderline() { if textUnderline.count > 1 { textUnderline.removeLast() } }
86
+ func dropLastTextDoubleStrike() { if textDoubleStrike.count > 1 { textDoubleStrike.removeLast() } }
87
+
88
+ func parse() throws -> [PrinterTextParserLine] {
89
+ try JavaString.splitLines(text).map { try PrinterTextParserLine(textParser: self, textLine: $0) }
90
+ }
91
+ }
92
+
93
+ final class PrinterTextParserLine {
94
+ let textParser: PrinterTextParser
95
+ let nbrColumns: Int
96
+ var nbrCharColumn: Int
97
+ var nbrCharForgetted: Int
98
+ var nbrCharColumnExceeded: Int
99
+ private(set) var columns: [PrinterTextParserColumn] = []
100
+
101
+ init(textParser: PrinterTextParser, textLine: JavaString) throws {
102
+ self.textParser = textParser
103
+ let nbrCharactersPerLine = textParser.printer.printerNbrCharactersPerLine
104
+
105
+ var columnsList: [JavaString] = []
106
+ var lastPosition = 0
107
+ for startPosition in PrinterTextParserLine.alignTagPositions(textLine) where startPosition > 0 {
108
+ columnsList.append(try textLine.substring(lastPosition, startPosition))
109
+ lastPosition = startPosition
110
+ }
111
+ columnsList.append(try textLine.substring(lastPosition))
112
+
113
+ nbrColumns = columnsList.count
114
+ nbrCharColumn = JavaMath.floorDiv(Float(nbrCharactersPerLine), Float(nbrColumns))
115
+ nbrCharForgetted = nbrCharactersPerLine - nbrCharColumn * nbrColumns
116
+ nbrCharColumnExceeded = 0
117
+
118
+ for column in columnsList {
119
+ columns.append(try PrinterTextParserColumn(textParserLine: self, textColumn: column))
120
+ }
121
+ }
122
+
123
+ private static func alignTagPositions(_ line: JavaString) -> [Int] {
124
+ let tags = PrinterTextParser.tagsAlign.map { Array("[\($0)]".utf16) }
125
+ var positions: [Int] = []
126
+ var index = 0
127
+ let units = line.units
128
+ while index + 3 <= units.count {
129
+ if tags.contains(where: { units[index..<(index + 3)].elementsEqual($0) }) {
130
+ positions.append(index)
131
+ index += 3
132
+ } else {
133
+ index += 1
134
+ }
135
+ }
136
+ return positions
137
+ }
138
+ }
139
+
140
+ final class PrinterTextParserColumn {
141
+ private static func generateSpace(_ count: Int) -> JavaString {
142
+ JavaString(String(repeating: " ", count: max(0, count)))
143
+ }
144
+
145
+ unowned let textParserLine: PrinterTextParserLine
146
+ private(set) var elements: [PrinterTextParserElement] = []
147
+
148
+ init(textParserLine: PrinterTextParserLine, textColumn input: JavaString) throws {
149
+ self.textParserLine = textParserLine
150
+ let textParser = textParserLine.textParser
151
+ var textAlign = PrinterTextParser.tagsAlignLeft
152
+ let textUnderlineStartColumn = textParser.lastTextUnderline
153
+ let textDoubleStrikeStartColumn = textParser.lastTextDoubleStrike
154
+ let textColorStartColumn = textParser.lastTextColor
155
+ let textReverseColorStartColumn = textParser.lastTextReverseColor
156
+
157
+ var textColumn = input
158
+ if textColumn.length > 2 {
159
+ let prefix = try textColumn.substring(0, 3).uppercased()
160
+ if prefix == "[L]" || prefix == "[C]" || prefix == "[R]" {
161
+ textAlign = try textColumn.substring(1, 2).uppercased().string
162
+ textColumn = try textColumn.substring(3)
163
+ }
164
+ }
165
+
166
+ let trimmedTextColumn = textColumn.trim()
167
+ var isImgOrBarcodeLine = false
168
+
169
+ if textParserLine.nbrColumns == 1 && trimmedTextColumn.indexOf("<") == 0 {
170
+ let openTagIndex = trimmedTextColumn.indexOf("<")
171
+ let openTagEndIndex = trimmedTextColumn.indexOf(">", from: openTagIndex + 1) + 1
172
+ if openTagIndex < openTagEndIndex {
173
+ let textParserTag = try PrinterTextParserTag(try trimmedTextColumn.substring(openTagIndex, openTagEndIndex))
174
+ let tagName = textParserTag.tagName.string
175
+ if tagName == PrinterTextParser.tagsImage || tagName == PrinterTextParser.tagsBarcode || tagName == PrinterTextParser.tagsQRCode {
176
+ let closeTag = "</\(tagName)>"
177
+ let closeTagPosition = trimmedTextColumn.length - JavaString(closeTag).length
178
+ if try trimmedTextColumn.substring(closeTagPosition) == closeTag {
179
+ let content = try trimmedTextColumn.substring(openTagEndIndex, closeTagPosition)
180
+ switch tagName {
181
+ case PrinterTextParser.tagsImage:
182
+ appendElement(try PrinterTextParserImg(column: self, textAlign: textAlign, hexadecimalString: content))
183
+ case PrinterTextParser.tagsBarcode:
184
+ appendElement(try PrinterTextParserBarcode(column: self, textAlign: textAlign, attributes: textParserTag.attributes, code: content))
185
+ default:
186
+ appendElement(try PrinterTextParserImg.qrCode(column: self, textAlign: textAlign, attributes: textParserTag.attributes, data: content))
187
+ }
188
+ isImgOrBarcodeLine = true
189
+ }
190
+ }
191
+ }
192
+ }
193
+
194
+ guard !isImgOrBarcodeLine else { return }
195
+
196
+ var offset = 0
197
+ while true {
198
+ var openTagIndex = textColumn.indexOf("<", from: offset)
199
+ var closeTagIndex = -1
200
+ if openTagIndex != -1 {
201
+ closeTagIndex = textColumn.indexOf(">", from: openTagIndex)
202
+ } else {
203
+ openTagIndex = textColumn.length
204
+ }
205
+ appendString(try textColumn.substring(offset, openTagIndex))
206
+ if closeTagIndex == -1 {
207
+ break
208
+ }
209
+ closeTagIndex += 1
210
+ let textParserTag = try PrinterTextParserTag(try textColumn.substring(openTagIndex, closeTagIndex))
211
+ if try PrinterTextParser.isTagTextFormat(textParserTag.tagName) {
212
+ applyFormatTag(textParserTag, textParser: textParser)
213
+ offset = closeTagIndex
214
+ } else {
215
+ appendString(JavaString("<"))
216
+ offset = openTagIndex + 1
217
+ }
218
+ }
219
+
220
+ var nbrCharForgetted = textParserLine.nbrCharForgetted
221
+ var nbrCharColumnExceeded = textParserLine.nbrCharColumnExceeded
222
+ let nbrCharColumn = textParserLine.nbrCharColumn
223
+ var nbrCharTextWithoutTag = 0
224
+ var leftSpace = 0
225
+ var rightSpace = 0
226
+
227
+ for element in elements {
228
+ nbrCharTextWithoutTag += try element.length()
229
+ }
230
+
231
+ switch textAlign {
232
+ case PrinterTextParser.tagsAlignLeft:
233
+ rightSpace = nbrCharColumn - nbrCharTextWithoutTag
234
+ case PrinterTextParser.tagsAlignCenter:
235
+ leftSpace = JavaMath.floorDiv(Float(nbrCharColumn) - Float(nbrCharTextWithoutTag), 2)
236
+ rightSpace = nbrCharColumn - nbrCharTextWithoutTag - leftSpace
237
+ case PrinterTextParser.tagsAlignRight:
238
+ leftSpace = nbrCharColumn - nbrCharTextWithoutTag
239
+ default:
240
+ break
241
+ }
242
+
243
+ if nbrCharForgetted > 0 {
244
+ nbrCharForgetted -= 1
245
+ rightSpace += 1
246
+ }
247
+
248
+ if nbrCharColumnExceeded < 0 {
249
+ leftSpace += nbrCharColumnExceeded
250
+ nbrCharColumnExceeded = 0
251
+ if leftSpace < 1 {
252
+ rightSpace += leftSpace - 1
253
+ leftSpace = 1
254
+ }
255
+ }
256
+
257
+ if leftSpace < 0 {
258
+ nbrCharColumnExceeded += leftSpace
259
+ leftSpace = 0
260
+ }
261
+ if rightSpace < 0 {
262
+ nbrCharColumnExceeded += rightSpace
263
+ rightSpace = 0
264
+ }
265
+
266
+ if leftSpace > 0 {
267
+ prependElement(PrinterTextParserString(
268
+ column: self,
269
+ text: PrinterTextParserColumn.generateSpace(leftSpace),
270
+ textSize: EscPosPrinterCommands.textSizeNormal,
271
+ textColor: textColorStartColumn,
272
+ textReverseColor: textReverseColorStartColumn,
273
+ textBold: EscPosPrinterCommands.textWeightNormal,
274
+ textUnderline: textUnderlineStartColumn,
275
+ textDoubleStrike: textDoubleStrikeStartColumn
276
+ ))
277
+ }
278
+ if rightSpace > 0 {
279
+ appendElement(PrinterTextParserString(
280
+ column: self,
281
+ text: PrinterTextParserColumn.generateSpace(rightSpace),
282
+ textSize: EscPosPrinterCommands.textSizeNormal,
283
+ textColor: textParser.lastTextColor,
284
+ textReverseColor: textParser.lastTextReverseColor,
285
+ textBold: EscPosPrinterCommands.textWeightNormal,
286
+ textUnderline: textParser.lastTextUnderline,
287
+ textDoubleStrike: textParser.lastTextDoubleStrike
288
+ ))
289
+ }
290
+
291
+ textParserLine.nbrCharForgetted = nbrCharForgetted
292
+ textParserLine.nbrCharColumnExceeded = nbrCharColumnExceeded
293
+ }
294
+
295
+ private func applyFormatTag(_ tag: PrinterTextParserTag, textParser: PrinterTextParser) {
296
+ let name = tag.tagName.string
297
+ if tag.isCloseTag {
298
+ switch name {
299
+ case PrinterTextParser.tagsFormatTextBold:
300
+ textParser.dropTextBold()
301
+ case PrinterTextParser.tagsFormatTextUnderline:
302
+ textParser.dropLastTextUnderline()
303
+ textParser.dropLastTextDoubleStrike()
304
+ case PrinterTextParser.tagsFormatTextFont:
305
+ textParser.dropLastTextSize()
306
+ textParser.dropLastTextColor()
307
+ textParser.dropLastTextReverseColor()
308
+ default:
309
+ break
310
+ }
311
+ return
312
+ }
313
+ switch name {
314
+ case PrinterTextParser.tagsFormatTextBold:
315
+ textParser.addTextBold(EscPosPrinterCommands.textWeightBold)
316
+ case PrinterTextParser.tagsFormatTextUnderline:
317
+ if let type = tag.attributes[PrinterTextParser.attrFormatTextUnderlineType] {
318
+ switch type {
319
+ case PrinterTextParser.attrFormatTextUnderlineTypeNormal:
320
+ textParser.addTextUnderline(EscPosPrinterCommands.textUnderlineLarge)
321
+ textParser.addTextDoubleStrike(textParser.lastTextDoubleStrike)
322
+ case PrinterTextParser.attrFormatTextUnderlineTypeDouble:
323
+ textParser.addTextUnderline(textParser.lastTextUnderline)
324
+ textParser.addTextDoubleStrike(EscPosPrinterCommands.textDoubleStrikeOn)
325
+ default:
326
+ break
327
+ }
328
+ } else {
329
+ textParser.addTextUnderline(EscPosPrinterCommands.textUnderlineLarge)
330
+ textParser.addTextDoubleStrike(textParser.lastTextDoubleStrike)
331
+ }
332
+ case PrinterTextParser.tagsFormatTextFont:
333
+ if let size = tag.attributes[PrinterTextParser.attrFormatTextFontSize] {
334
+ switch size {
335
+ case "tall": textParser.addTextSize(EscPosPrinterCommands.textSizeDoubleHeight)
336
+ case "wide": textParser.addTextSize(EscPosPrinterCommands.textSizeDoubleWidth)
337
+ case "big": textParser.addTextSize(EscPosPrinterCommands.textSizeBig)
338
+ case "big-2": textParser.addTextSize(EscPosPrinterCommands.textSizeBig2)
339
+ case "big-3": textParser.addTextSize(EscPosPrinterCommands.textSizeBig3)
340
+ case "big-4": textParser.addTextSize(EscPosPrinterCommands.textSizeBig4)
341
+ case "big-5": textParser.addTextSize(EscPosPrinterCommands.textSizeBig5)
342
+ case "big-6": textParser.addTextSize(EscPosPrinterCommands.textSizeBig6)
343
+ default: textParser.addTextSize(EscPosPrinterCommands.textSizeNormal)
344
+ }
345
+ } else {
346
+ textParser.addTextSize(textParser.lastTextSize)
347
+ }
348
+ if let color = tag.attributes[PrinterTextParser.attrFormatTextFontColor] {
349
+ switch color {
350
+ case "bg-black":
351
+ textParser.addTextColor(EscPosPrinterCommands.textColorBlack)
352
+ textParser.addTextReverseColor(EscPosPrinterCommands.textColorReverseOn)
353
+ case "red":
354
+ textParser.addTextColor(EscPosPrinterCommands.textColorRed)
355
+ textParser.addTextReverseColor(EscPosPrinterCommands.textColorReverseOff)
356
+ case "bg-red":
357
+ textParser.addTextColor(EscPosPrinterCommands.textColorRed)
358
+ textParser.addTextReverseColor(EscPosPrinterCommands.textColorReverseOn)
359
+ default:
360
+ textParser.addTextColor(EscPosPrinterCommands.textColorBlack)
361
+ textParser.addTextReverseColor(EscPosPrinterCommands.textColorReverseOff)
362
+ }
363
+ } else {
364
+ textParser.addTextColor(textParser.lastTextColor)
365
+ textParser.addTextReverseColor(textParser.lastTextReverseColor)
366
+ }
367
+ default:
368
+ break
369
+ }
370
+ }
371
+
372
+ private func appendString(_ text: JavaString) {
373
+ let textParser = textParserLine.textParser
374
+ appendElement(PrinterTextParserString(
375
+ column: self,
376
+ text: text,
377
+ textSize: textParser.lastTextSize,
378
+ textColor: textParser.lastTextColor,
379
+ textReverseColor: textParser.lastTextReverseColor,
380
+ textBold: textParser.lastTextBold,
381
+ textUnderline: textParser.lastTextUnderline,
382
+ textDoubleStrike: textParser.lastTextDoubleStrike
383
+ ))
384
+ }
385
+
386
+ private func prependElement(_ element: PrinterTextParserElement) {
387
+ elements.insert(element, at: 0)
388
+ }
389
+
390
+ private func appendElement(_ element: PrinterTextParserElement) {
391
+ elements.append(element)
392
+ }
393
+ }
394
+
395
+ final class PrinterTextParserTag {
396
+ private(set) var tagName = JavaString("")
397
+ private(set) var attributes: [String: String] = [:]
398
+ private(set) var length = 0
399
+ private(set) var isCloseTag = false
400
+
401
+ init(_ rawTag: JavaString) throws {
402
+ let tag = rawTag.trim()
403
+ guard try tag.substring(0, 1) == "<", try tag.substring(tag.length - 1) == ">" else {
404
+ return
405
+ }
406
+ length = tag.length
407
+ let openTagIndex = tag.indexOf("<")
408
+ let closeTagIndex = tag.indexOf(">")
409
+ let nextSpaceIndex = tag.indexOf(" ")
410
+ if nextSpaceIndex != -1 && nextSpaceIndex < closeTagIndex {
411
+ tagName = try tag.substring(openTagIndex + 1, nextSpaceIndex).lowercased()
412
+ var attributesString = try tag.substring(nextSpaceIndex, closeTagIndex).trim()
413
+ while attributesString.contains("='") {
414
+ let egalPos = attributesString.indexOf("='")
415
+ let endPos = attributesString.indexOf("'", from: egalPos + 2)
416
+ let attributeName = try attributesString.substring(0, egalPos)
417
+ let attributeValue = try attributesString.substring(egalPos + 2, endPos)
418
+ if !attributeName.isEmpty {
419
+ attributes[attributeName.string] = attributeValue.string
420
+ }
421
+ attributesString = try attributesString.substring(endPos + 1).trim()
422
+ }
423
+ } else {
424
+ tagName = try tag.substring(openTagIndex + 1, closeTagIndex).lowercased()
425
+ }
426
+ if try tagName.substring(0, 1) == "/" {
427
+ tagName = try tagName.substring(1)
428
+ isCloseTag = true
429
+ }
430
+ }
431
+ }
432
+
433
+ final class PrinterTextParserString: PrinterTextParserElement {
434
+ private let printer: EscPosPrinter
435
+ private let text: JavaString
436
+ private let textSize: [UInt8]
437
+ private let textColor: [UInt8]
438
+ private let textReverseColor: [UInt8]
439
+ private let textBold: [UInt8]
440
+ private let textUnderline: [UInt8]
441
+ private let textDoubleStrike: [UInt8]
442
+
443
+ init(column: PrinterTextParserColumn, text: JavaString, textSize: [UInt8], textColor: [UInt8], textReverseColor: [UInt8], textBold: [UInt8], textUnderline: [UInt8], textDoubleStrike: [UInt8]) {
444
+ self.printer = column.textParserLine.textParser.printer
445
+ self.text = text
446
+ self.textSize = textSize
447
+ self.textColor = textColor
448
+ self.textReverseColor = textReverseColor
449
+ self.textBold = textBold
450
+ self.textUnderline = textUnderline
451
+ self.textDoubleStrike = textDoubleStrike
452
+ }
453
+
454
+ func length() throws -> Int {
455
+ var coefficient = 1
456
+ switch textSize {
457
+ case EscPosPrinterCommands.textSizeDoubleWidth, EscPosPrinterCommands.textSizeBig:
458
+ coefficient = 2
459
+ case EscPosPrinterCommands.textSizeBig2:
460
+ coefficient = 3
461
+ case EscPosPrinterCommands.textSizeBig3:
462
+ coefficient = 4
463
+ case EscPosPrinterCommands.textSizeBig4:
464
+ coefficient = 5
465
+ case EscPosPrinterCommands.textSizeBig5:
466
+ coefficient = 6
467
+ case EscPosPrinterCommands.textSizeBig6:
468
+ coefficient = 7
469
+ default:
470
+ break
471
+ }
472
+ if let encoding = printer.encoding {
473
+ return try encoding.encode(text).count * coefficient
474
+ }
475
+ return text.length * coefficient
476
+ }
477
+
478
+ func print(_ printerSocket: EscPosPrinterCommands) throws {
479
+ try printerSocket.printText(
480
+ text,
481
+ textSize: textSize,
482
+ textColor: textColor,
483
+ textReverseColor: textReverseColor,
484
+ textBold: textBold,
485
+ textUnderline: textUnderline,
486
+ textDoubleStrike: textDoubleStrike
487
+ )
488
+ }
489
+ }
490
+
491
+ final class PrinterTextParserImg: PrinterTextParserElement {
492
+ static func bitmapToHexadecimalString(_ printerSize: EscPosPrinterSize, _ bitmap: EscPosBitmap) -> String {
493
+ EscPosHex.string(printerSize.bitmapToBytes(bitmap))
494
+ }
495
+
496
+ static func qrCode(column: PrinterTextParserColumn, textAlign: String, attributes: [String: String], data: JavaString) throws -> PrinterTextParserImg {
497
+ let printer = column.textParserLine.textParser.printer
498
+ let content = data.trim()
499
+ var size = printer.mmToPx(20)
500
+ if let value = attributes[PrinterTextParser.attrQRCodeSize] {
501
+ guard let millimeters = JavaMath.parseFloat(value) else {
502
+ throw EscPosError.parser("Invalid QR code size value")
503
+ }
504
+ size = printer.mmToPx(millimeters)
505
+ }
506
+ return try PrinterTextParserImg(column: column, textAlign: textAlign, image: EscPosPrinterCommands.qrCodeDataToBytes(content.string, size: size))
507
+ }
508
+
509
+ private let elementLength: Int
510
+ let image: [UInt8]
511
+
512
+ convenience init(column: PrinterTextParserColumn, textAlign: String, hexadecimalString: JavaString) throws {
513
+ try self.init(column: column, textAlign: textAlign, image: EscPosHex.bytes(hexadecimalString))
514
+ }
515
+
516
+ init(column: PrinterTextParserColumn, textAlign: String, image source: [UInt8]) throws {
517
+ let printer = column.textParserLine.textParser.printer
518
+ guard source.count >= 8 else {
519
+ throw EscPosError.runtime("Index \(max(source.count, 4)) out of bounds for length \(source.count)")
520
+ }
521
+ var image = source
522
+ let byteWidth = Int(image[4]) + Int(image[5]) * 256
523
+ let width = byteWidth * 8
524
+ let height = Int(image[6]) + Int(image[7]) * 256
525
+ let nbrByteDiff = JavaMath.floorDiv(Float(printer.printerWidthPx - width), 8)
526
+ var nbrWhiteByteToInsert = 0
527
+ switch textAlign {
528
+ case PrinterTextParser.tagsAlignCenter:
529
+ nbrWhiteByteToInsert = JavaMath.round(Float(nbrByteDiff) / 2)
530
+ case PrinterTextParser.tagsAlignRight:
531
+ nbrWhiteByteToInsert = nbrByteDiff
532
+ default:
533
+ break
534
+ }
535
+
536
+ if nbrWhiteByteToInsert > 0 {
537
+ let newByteWidth = byteWidth + nbrWhiteByteToInsert
538
+ var newImage = EscPosPrinterCommands.initGSv0Command(bytesByLine: newByteWidth, bitmapHeight: height)
539
+ for row in 0..<height {
540
+ let sourceStart = byteWidth * row + 8
541
+ let destinationStart = newByteWidth * row + nbrWhiteByteToInsert + 8
542
+ guard sourceStart + byteWidth <= image.count else {
543
+ throw EscPosError.runtime("arraycopy: last source index \(sourceStart + byteWidth) out of bounds for byte[\(image.count)]")
544
+ }
545
+ newImage.replaceSubrange(destinationStart..<(destinationStart + byteWidth), with: image[sourceStart..<(sourceStart + byteWidth)])
546
+ }
547
+ image = newImage
548
+ }
549
+
550
+ elementLength = JavaMath.toInt((Float(byteWidth) * 8 / Float(printer.printerCharSizeWidthPx)).rounded(.up))
551
+ self.image = image
552
+ }
553
+
554
+ func length() throws -> Int {
555
+ elementLength
556
+ }
557
+
558
+ func print(_ printerSocket: EscPosPrinterCommands) throws {
559
+ try printerSocket.printImage(image)
560
+ }
561
+ }
562
+
563
+ final class PrinterTextParserBarcode: PrinterTextParserElement {
564
+ private let barcode: EscPosBarcode
565
+ private let elementLength: Int
566
+ private let align: [UInt8]
567
+
568
+ init(column: PrinterTextParserColumn, textAlign: String, attributes: [String: String], code rawCode: JavaString) throws {
569
+ let printer = column.textParserLine.textParser.printer
570
+ let code = rawCode.trim()
571
+
572
+ switch textAlign {
573
+ case PrinterTextParser.tagsAlignCenter:
574
+ align = EscPosPrinterCommands.textAlignCenter
575
+ case PrinterTextParser.tagsAlignRight:
576
+ align = EscPosPrinterCommands.textAlignRight
577
+ default:
578
+ align = EscPosPrinterCommands.textAlignLeft
579
+ }
580
+
581
+ elementLength = printer.printerNbrCharactersPerLine
582
+
583
+ var height: Float = 10
584
+ if let value = attributes[PrinterTextParser.attrBarcodeHeight] {
585
+ guard let parsed = JavaMath.parseFloat(value) else {
586
+ throw EscPosError.parser("Invalid barcode height value")
587
+ }
588
+ height = parsed
589
+ }
590
+
591
+ var width: Float = 0
592
+ if let value = attributes[PrinterTextParser.attrBarcodeWidth] {
593
+ guard let parsed = JavaMath.parseFloat(value) else {
594
+ throw EscPosError.parser("Invalid barcode width value")
595
+ }
596
+ width = parsed
597
+ }
598
+
599
+ var textPosition = EscPosPrinterCommands.barcodeTextPositionBelow
600
+ if let value = attributes[PrinterTextParser.attrBarcodeTextPosition] {
601
+ switch value {
602
+ case PrinterTextParser.attrBarcodeTextPositionNone:
603
+ textPosition = EscPosPrinterCommands.barcodeTextPositionNone
604
+ case PrinterTextParser.attrBarcodeTextPositionAbove:
605
+ textPosition = EscPosPrinterCommands.barcodeTextPositionAbove
606
+ default:
607
+ break
608
+ }
609
+ }
610
+
611
+ let barcodeType = attributes[PrinterTextParser.attrBarcodeType] ?? PrinterTextParser.attrBarcodeTypeEAN13
612
+ barcode = try EscPosBarcode.make(type: barcodeType, printerSize: printer, code: code, widthMM: width, heightMM: height, textPosition: textPosition)
613
+ }
614
+
615
+ func length() throws -> Int {
616
+ elementLength
617
+ }
618
+
619
+ func print(_ printerSocket: EscPosPrinterCommands) throws {
620
+ try printerSocket.setAlign(align).printBarcode(barcode)
621
+ }
622
+ }