@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,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
|
+
}
|