seven365-zyprinter 1.0.8 → 1.0.10
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/ios/Sources/Plugin/ZywellSDK.swift +170 -33
- package/package.json +1 -1
|
@@ -13,6 +13,15 @@ import ExternalAccessory
|
|
|
13
13
|
|
|
14
14
|
@objc public class ZywellSDK: NSObject {
|
|
15
15
|
|
|
16
|
+
// MARK: - Printer Configuration
|
|
17
|
+
|
|
18
|
+
/// Default printer width in characters (for 80mm thermal paper)
|
|
19
|
+
/// Common widths: 32 chars (58mm paper), 48 chars (80mm paper)
|
|
20
|
+
private let printerWidth: Int = 48
|
|
21
|
+
|
|
22
|
+
/// Default separator character (can be overridden per-template)
|
|
23
|
+
private let defaultSeparatorChar: String = "-"
|
|
24
|
+
|
|
16
25
|
private var bleManager: POSBLEManager?
|
|
17
26
|
private var wifiManagers: [String: POSWIFIManager] = [:]
|
|
18
27
|
private var discoveredPeripherals: [CBPeripheral] = []
|
|
@@ -393,6 +402,9 @@ import ExternalAccessory
|
|
|
393
402
|
private func formatReceiptForPrinter(template: [String: Any]) -> Data? {
|
|
394
403
|
var printData = Data()
|
|
395
404
|
|
|
405
|
+
// Get separator character from template (dynamic) or use default
|
|
406
|
+
let separatorChar = getString(template["separator"]) ?? defaultSeparatorChar
|
|
407
|
+
|
|
396
408
|
// Initialize printer
|
|
397
409
|
printData.append(Data([0x1B, 0x40]))
|
|
398
410
|
|
|
@@ -451,22 +463,45 @@ import ExternalAccessory
|
|
|
451
463
|
}
|
|
452
464
|
}
|
|
453
465
|
|
|
454
|
-
// Left align (ESC a 0)
|
|
466
|
+
// Left align for separator (ESC a 0)
|
|
455
467
|
printData.append(Data([0x1B, 0x61, 0x00]))
|
|
456
468
|
|
|
457
|
-
// Separator
|
|
458
|
-
if let separatorData = (
|
|
469
|
+
// Separator (normal font size)
|
|
470
|
+
if let separatorData = generateSeparator(withChar: separatorChar).data(using: .utf8) {
|
|
459
471
|
printData.append(separatorData)
|
|
460
472
|
}
|
|
461
473
|
|
|
462
474
|
// ========== ORDER INFO SECTION ==========
|
|
463
|
-
//
|
|
475
|
+
// Center align for order info (ESC a 1)
|
|
476
|
+
printData.append(Data([0x1B, 0x61, 0x01]))
|
|
477
|
+
|
|
478
|
+
// Template type (e.g., "Kitchen Tickets") - normal size
|
|
464
479
|
if let orderType = getString(template["order_type"]),
|
|
465
480
|
let orderTypeData = (orderType + "\n").data(using: .utf8) {
|
|
466
481
|
printData.append(orderTypeData)
|
|
467
482
|
}
|
|
468
483
|
|
|
469
|
-
// Table and order number
|
|
484
|
+
// Table and order number - Read formatting from order_info or use defaults
|
|
485
|
+
var orderInfoSizeCode: UInt8 = 0x11 // Default to 2x (large)
|
|
486
|
+
var orderInfoBold = true // Default to bold
|
|
487
|
+
|
|
488
|
+
if let orderInfo = template["order_info"] as? [String: Any] {
|
|
489
|
+
if let size = orderInfo["size"] {
|
|
490
|
+
orderInfoSizeCode = mapHeaderSizeToCode(size)
|
|
491
|
+
}
|
|
492
|
+
if let bold = orderInfo["bold"] as? Bool {
|
|
493
|
+
orderInfoBold = bold
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Apply order info formatting
|
|
498
|
+
if orderInfoBold {
|
|
499
|
+
printData.append(Data([0x1B, 0x45, 0x01])) // Bold on
|
|
500
|
+
}
|
|
501
|
+
if orderInfoSizeCode != 0x00 {
|
|
502
|
+
printData.append(Data([0x1D, 0x21, orderInfoSizeCode])) // Set size from config
|
|
503
|
+
}
|
|
504
|
+
|
|
470
505
|
var orderInfoLine = ""
|
|
471
506
|
if let tableName = getString(template["table_name"]) {
|
|
472
507
|
orderInfoLine += tableName
|
|
@@ -482,16 +517,26 @@ import ExternalAccessory
|
|
|
482
517
|
printData.append(orderInfoData)
|
|
483
518
|
}
|
|
484
519
|
|
|
520
|
+
// Reset formatting back to normal
|
|
521
|
+
if orderInfoSizeCode != 0x00 {
|
|
522
|
+
printData.append(Data([0x1D, 0x21, 0x00])) // Normal size
|
|
523
|
+
}
|
|
524
|
+
if orderInfoBold {
|
|
525
|
+
printData.append(Data([0x1B, 0x45, 0x00])) // Bold off
|
|
526
|
+
}
|
|
485
527
|
|
|
486
|
-
//
|
|
487
|
-
|
|
528
|
+
// Left align for items section (ESC a 0)
|
|
529
|
+
printData.append(Data([0x1B, 0x61, 0x00]))
|
|
530
|
+
|
|
531
|
+
// Separator for items section (normal font size)
|
|
532
|
+
if let separatorData = generateSeparator(withChar: separatorChar).data(using: .utf8) {
|
|
488
533
|
printData.append(separatorData)
|
|
489
534
|
}
|
|
490
535
|
|
|
491
536
|
// ========== ITEMS SECTION (NEW STRUCTURE) ==========
|
|
492
537
|
if let kitchen = template["kitchen"] as? [[String: Any]] {
|
|
493
538
|
// Get item formatting from item object
|
|
494
|
-
var itemSizeCode: UInt8 =
|
|
539
|
+
var itemSizeCode: UInt8 = 0x11 // Default to 2x (large) for better readability
|
|
495
540
|
var itemBold = false
|
|
496
541
|
|
|
497
542
|
if let item = template["item"] as? [String: Any] {
|
|
@@ -608,8 +653,8 @@ import ExternalAccessory
|
|
|
608
653
|
}
|
|
609
654
|
|
|
610
655
|
// ========== TOTAL SECTION ==========
|
|
611
|
-
// Separator before total
|
|
612
|
-
if let separatorData = (
|
|
656
|
+
// Separator before total (normal font size)
|
|
657
|
+
if let separatorData = generateSeparator(withChar: separatorChar).data(using: .utf8) {
|
|
613
658
|
printData.append(separatorData)
|
|
614
659
|
}
|
|
615
660
|
|
|
@@ -636,8 +681,8 @@ import ExternalAccessory
|
|
|
636
681
|
|
|
637
682
|
// ========== TOTAL SECTION (NEW STRUCTURE) ==========
|
|
638
683
|
if let total = getString(template["total"]) {
|
|
639
|
-
// Separator
|
|
640
|
-
if let separatorData = (
|
|
684
|
+
// Separator (may need to account for total font size)
|
|
685
|
+
if let separatorData = generateSeparator(withChar: separatorChar).data(using: .utf8) {
|
|
641
686
|
printData.append(separatorData)
|
|
642
687
|
}
|
|
643
688
|
|
|
@@ -675,8 +720,8 @@ import ExternalAccessory
|
|
|
675
720
|
}
|
|
676
721
|
}
|
|
677
722
|
|
|
678
|
-
// Separator
|
|
679
|
-
if let separatorData = (
|
|
723
|
+
// Separator (normal font size)
|
|
724
|
+
if let separatorData = generateSeparator(withChar: separatorChar).data(using: .utf8) {
|
|
680
725
|
printData.append(separatorData)
|
|
681
726
|
}
|
|
682
727
|
|
|
@@ -686,8 +731,8 @@ import ExternalAccessory
|
|
|
686
731
|
printData.append(paymentData)
|
|
687
732
|
}
|
|
688
733
|
|
|
689
|
-
// Separator
|
|
690
|
-
if let separatorData = (
|
|
734
|
+
// Separator (normal font size)
|
|
735
|
+
if let separatorData = generateSeparator(withChar: separatorChar).data(using: .utf8) {
|
|
691
736
|
printData.append(separatorData)
|
|
692
737
|
}
|
|
693
738
|
|
|
@@ -723,10 +768,21 @@ import ExternalAccessory
|
|
|
723
768
|
printData.append(messageData)
|
|
724
769
|
}
|
|
725
770
|
|
|
726
|
-
// Reset footer formatting
|
|
771
|
+
// Reset footer formatting before timestamp
|
|
727
772
|
if footerSizeCode != 0x00 {
|
|
728
773
|
printData.append(Data([0x1D, 0x21, 0x00])) // Normal size
|
|
729
774
|
}
|
|
775
|
+
if footerBold {
|
|
776
|
+
printData.append(Data([0x1B, 0x45, 0x00])) // Bold off
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
// Print timestamp
|
|
780
|
+
let dateFormat = getString(footer["date_format"]) ?? "YYYY-MM-DD"
|
|
781
|
+
let timeFormat = getString(footer["time_format"]) ?? "24H"
|
|
782
|
+
let timestamp = formatTimestamp(dateFormat: dateFormat, timeFormat: timeFormat)
|
|
783
|
+
if let timestampData = (timestamp + "\n").data(using: .utf8) {
|
|
784
|
+
printData.append(timestampData)
|
|
785
|
+
}
|
|
730
786
|
}
|
|
731
787
|
|
|
732
788
|
// Line feeds
|
|
@@ -741,32 +797,40 @@ import ExternalAccessory
|
|
|
741
797
|
// MARK: - Helper Functions
|
|
742
798
|
|
|
743
799
|
private func mapHeaderSizeToCode(_ size: Any) -> UInt8 {
|
|
800
|
+
var resultCode: UInt8 = 0x00
|
|
801
|
+
|
|
744
802
|
if let sizeInt = size as? Int {
|
|
745
803
|
switch sizeInt {
|
|
746
|
-
case 1:
|
|
747
|
-
case 2:
|
|
748
|
-
case 3:
|
|
749
|
-
case 4:
|
|
750
|
-
default:
|
|
804
|
+
case 1: resultCode = 0x00
|
|
805
|
+
case 2: resultCode = 0x11
|
|
806
|
+
case 3: resultCode = 0x22
|
|
807
|
+
case 4: resultCode = 0x33
|
|
808
|
+
default: resultCode = 0x00
|
|
751
809
|
}
|
|
810
|
+
print("ZywellSDK: Mapped size Int(\(sizeInt)) → 0x\(String(format: "%02X", resultCode))")
|
|
752
811
|
} else if let sizeStr = size as? String {
|
|
753
812
|
switch sizeStr.lowercased() {
|
|
754
|
-
case "normal", "1":
|
|
755
|
-
case "large", "2":
|
|
756
|
-
case "xlarge", "3":
|
|
757
|
-
case "4":
|
|
758
|
-
default:
|
|
813
|
+
case "normal", "1": resultCode = 0x00
|
|
814
|
+
case "large", "2": resultCode = 0x11
|
|
815
|
+
case "xlarge", "3": resultCode = 0x22
|
|
816
|
+
case "4": resultCode = 0x33
|
|
817
|
+
default: resultCode = 0x00
|
|
759
818
|
}
|
|
819
|
+
print("ZywellSDK: Mapped size String(\"\(sizeStr)\") → 0x\(String(format: "%02X", resultCode))")
|
|
760
820
|
} else if let sizeNum = size as? NSNumber {
|
|
761
821
|
switch sizeNum.intValue {
|
|
762
|
-
case 1:
|
|
763
|
-
case 2:
|
|
764
|
-
case 3:
|
|
765
|
-
case 4:
|
|
766
|
-
default:
|
|
822
|
+
case 1: resultCode = 0x00
|
|
823
|
+
case 2: resultCode = 0x11
|
|
824
|
+
case 3: resultCode = 0x22
|
|
825
|
+
case 4: resultCode = 0x33
|
|
826
|
+
default: resultCode = 0x00
|
|
767
827
|
}
|
|
828
|
+
print("ZywellSDK: Mapped size NSNumber(\(sizeNum.intValue)) → 0x\(String(format: "%02X", resultCode))")
|
|
829
|
+
} else {
|
|
830
|
+
print("ZywellSDK: Unknown size type: \(type(of: size)), defaulting to 0x00")
|
|
768
831
|
}
|
|
769
|
-
|
|
832
|
+
|
|
833
|
+
return resultCode
|
|
770
834
|
}
|
|
771
835
|
|
|
772
836
|
private func getBool(_ value: Any?) -> Bool {
|
|
@@ -788,6 +852,79 @@ import ExternalAccessory
|
|
|
788
852
|
return String(describing: value)
|
|
789
853
|
}
|
|
790
854
|
|
|
855
|
+
/**
|
|
856
|
+
* Formats the current timestamp based on date and time format preferences
|
|
857
|
+
* - Parameters:
|
|
858
|
+
* - dateFormat: Format string like "YYYY-MM-DD", "DD-MM-YYYY", "MM-DD-YYYY"
|
|
859
|
+
* - timeFormat: "12H" or "24H"
|
|
860
|
+
* - Returns: Formatted timestamp string
|
|
861
|
+
*/
|
|
862
|
+
private func formatTimestamp(dateFormat: String, timeFormat: String) -> String {
|
|
863
|
+
let now = Date()
|
|
864
|
+
let calendar = Calendar.current
|
|
865
|
+
|
|
866
|
+
let year = calendar.component(.year, from: now)
|
|
867
|
+
let month = calendar.component(.month, from: now)
|
|
868
|
+
let day = calendar.component(.day, from: now)
|
|
869
|
+
let hour = calendar.component(.hour, from: now)
|
|
870
|
+
let minute = calendar.component(.minute, from: now)
|
|
871
|
+
let second = calendar.component(.second, from: now)
|
|
872
|
+
|
|
873
|
+
// Format date
|
|
874
|
+
var formattedDate: String
|
|
875
|
+
switch dateFormat {
|
|
876
|
+
case "DD-MM-YYYY", "DD/MM/YYYY":
|
|
877
|
+
formattedDate = String(format: "%02d-%02d-%04d", day, month, year)
|
|
878
|
+
case "MM-DD-YYYY", "MM/DD/YYYY":
|
|
879
|
+
formattedDate = String(format: "%02d-%02d-%04d", month, day, year)
|
|
880
|
+
default: // "YYYY-MM-DD"
|
|
881
|
+
formattedDate = String(format: "%04d-%02d-%02d", year, month, day)
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
// Format time
|
|
885
|
+
var formattedTime: String
|
|
886
|
+
if timeFormat == "12H" {
|
|
887
|
+
let hour12 = hour % 12 == 0 ? 12 : hour % 12
|
|
888
|
+
let ampm = hour >= 12 ? "PM" : "AM"
|
|
889
|
+
formattedTime = String(format: "%d:%02d:%02d %@", hour12, minute, second, ampm)
|
|
890
|
+
} else { // 24H
|
|
891
|
+
formattedTime = String(format: "%02d:%02d:%02d", hour, minute, second)
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
return "\(formattedDate) \(formattedTime)"
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
/**
|
|
898
|
+
* Generates a separator line based on current printer width and font magnification
|
|
899
|
+
* - Parameter sizeCode: The current font size code (0x00, 0x11, 0x22, 0x33)
|
|
900
|
+
* - Returns: A separator string with appropriate width
|
|
901
|
+
*
|
|
902
|
+
* Font magnification affects character width:
|
|
903
|
+
* - 0x00 (normal): 1x width
|
|
904
|
+
* - 0x11 (2x2): 2x width (half as many characters fit)
|
|
905
|
+
* - 0x22 (3x3): 3x width (one-third as many characters fit)
|
|
906
|
+
* - 0x33 (4x4): 4x width (one-quarter as many characters fit)
|
|
907
|
+
*/
|
|
908
|
+
private func generateSeparator(forSizeCode sizeCode: UInt8 = 0x00, withChar separatorChar: String? = nil) -> String {
|
|
909
|
+
// Calculate width divisor based on font magnification
|
|
910
|
+
let widthMultiplier: Int
|
|
911
|
+
switch sizeCode {
|
|
912
|
+
case 0x11: widthMultiplier = 2 // 2x width
|
|
913
|
+
case 0x22: widthMultiplier = 3 // 3x width
|
|
914
|
+
case 0x33: widthMultiplier = 4 // 4x width
|
|
915
|
+
default: widthMultiplier = 1 // Normal width
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// Calculate actual character count that fits on the line
|
|
919
|
+
let effectiveWidth = printerWidth / widthMultiplier
|
|
920
|
+
|
|
921
|
+
// Use provided separator character or fall back to default
|
|
922
|
+
let charToUse = separatorChar ?? defaultSeparatorChar
|
|
923
|
+
|
|
924
|
+
// Generate separator string
|
|
925
|
+
return String(repeating: charToUse, count: effectiveWidth) + "\n"
|
|
926
|
+
}
|
|
927
|
+
|
|
791
928
|
// MARK: - Callback Storage
|
|
792
929
|
|
|
793
930
|
private var discoveryCompletion: (([[String: Any]], String?) -> Void)?
|
package/package.json
CHANGED