seven365-zyprinter 1.0.9 → 1.1.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.
@@ -19,8 +19,8 @@ import ExternalAccessory
19
19
  /// Common widths: 32 chars (58mm paper), 48 chars (80mm paper)
20
20
  private let printerWidth: Int = 48
21
21
 
22
- /// Character separator character
23
- private let separatorChar: String = "-"
22
+ /// Default separator character (can be overridden per-template)
23
+ private let defaultSeparatorChar: String = "-"
24
24
 
25
25
  private var bleManager: POSBLEManager?
26
26
  private var wifiManagers: [String: POSWIFIManager] = [:]
@@ -402,6 +402,9 @@ import ExternalAccessory
402
402
  private func formatReceiptForPrinter(template: [String: Any]) -> Data? {
403
403
  var printData = Data()
404
404
 
405
+ // Get separator character from template (dynamic) or use default
406
+ let separatorChar = getString(template["separator"]) ?? defaultSeparatorChar
407
+
405
408
  // Initialize printer
406
409
  printData.append(Data([0x1B, 0x40]))
407
410
 
@@ -460,23 +463,57 @@ import ExternalAccessory
460
463
  }
461
464
  }
462
465
 
463
- // Left align (ESC a 0)
466
+ // Left align for separator (ESC a 0)
464
467
  printData.append(Data([0x1B, 0x61, 0x00]))
465
468
 
466
469
  // Separator (normal font size)
467
- if let separatorData = generateSeparator().data(using: .utf8) {
470
+ if let separatorData = generateSeparator(withChar: separatorChar).data(using: .utf8) {
468
471
  printData.append(separatorData)
469
472
  }
470
473
 
471
474
  // ========== ORDER INFO SECTION ==========
472
- // Template type (e.g., "Kitchen Tickets") - normal size
473
- if let orderType = getString(template["order_type"]),
474
- let orderTypeData = (orderType + "\n").data(using: .utf8) {
475
- printData.append(orderTypeData)
475
+ // Center align for order info (ESC a 1)
476
+ printData.append(Data([0x1B, 0x61, 0x01]))
477
+
478
+ // Template type (e.g., "Kitchen Tickets") - customizable formatting
479
+ if let orderType = getString(template["order_type"]) {
480
+ // Get order_type formatting from order_type_config object
481
+ var orderTypeSizeCode: UInt8 = 0x00 // Default to normal size
482
+ var orderTypeBold = false // Default to not bold
483
+
484
+ if let orderTypeConfig = template["order_type_config"] as? [String: Any] {
485
+ if let size = orderTypeConfig["size"] {
486
+ orderTypeSizeCode = mapHeaderSizeToCode(size)
487
+ }
488
+ if let bold = orderTypeConfig["bold"] as? Bool {
489
+ orderTypeBold = bold
490
+ }
491
+ }
492
+
493
+ // Apply order_type formatting
494
+ if orderTypeBold {
495
+ printData.append(Data([0x1B, 0x45, 0x01])) // Bold on
496
+ }
497
+ if orderTypeSizeCode != 0x00 {
498
+ printData.append(Data([0x1D, 0x21, orderTypeSizeCode])) // Set size
499
+ }
500
+
501
+ // Print order type
502
+ if let orderTypeData = (orderType + "\n").data(using: .utf8) {
503
+ printData.append(orderTypeData)
504
+ }
505
+
506
+ // Reset order_type formatting
507
+ if orderTypeSizeCode != 0x00 {
508
+ printData.append(Data([0x1D, 0x21, 0x00])) // Normal size
509
+ }
510
+ if orderTypeBold {
511
+ printData.append(Data([0x1B, 0x45, 0x00])) // Bold off
512
+ }
476
513
  }
477
514
 
478
515
  // Table and order number - Read formatting from order_info or use defaults
479
- var orderInfoSizeCode: UInt8 = 0x22 // Default to 3x (xlarge)
516
+ var orderInfoSizeCode: UInt8 = 0x11 // Default to 2x (large)
480
517
  var orderInfoBold = true // Default to bold
481
518
 
482
519
  if let orderInfo = template["order_info"] as? [String: Any] {
@@ -519,17 +556,18 @@ import ExternalAccessory
519
556
  printData.append(Data([0x1B, 0x45, 0x00])) // Bold off
520
557
  }
521
558
 
522
-
559
+ // Left align for items section (ESC a 0)
560
+ printData.append(Data([0x1B, 0x61, 0x00]))
523
561
 
524
562
  // Separator for items section (normal font size)
525
- if let separatorData = generateSeparator().data(using: .utf8) {
563
+ if let separatorData = generateSeparator(withChar: separatorChar).data(using: .utf8) {
526
564
  printData.append(separatorData)
527
565
  }
528
566
 
529
567
  // ========== ITEMS SECTION (NEW STRUCTURE) ==========
530
568
  if let kitchen = template["kitchen"] as? [[String: Any]] {
531
569
  // Get item formatting from item object
532
- var itemSizeCode: UInt8 = 0x00
570
+ var itemSizeCode: UInt8 = 0x11 // Default to 2x (large) for better readability
533
571
  var itemBold = false
534
572
 
535
573
  if let item = template["item"] as? [String: Any] {
@@ -545,14 +583,14 @@ import ExternalAccessory
545
583
  if itemBold {
546
584
  printData.append(Data([0x1B, 0x45, 0x01])) // Bold on
547
585
  }
548
- if itemSizeCode != 0x00 {
549
- printData.append(Data([0x1D, 0x21, itemSizeCode])) // Set size
550
- }
586
+ // Always send size command to ensure items are at configured size
587
+ printData.append(Data([0x1D, 0x21, itemSizeCode])) // Set size (even if 0x00 for normal)
551
588
 
552
589
  // Get modifier formatting from modifier object
553
590
  var modifierStyle = "bullet" // default
554
591
  var modifierSizeCode: UInt8 = 0x00
555
592
  var modifierIndent = " " // default: medium
593
+ var modifierBold = false // NEW: bold support for modifiers
556
594
 
557
595
  if let modifier = template["modifier"] as? [String: Any] {
558
596
  if let style = getString(modifier["style"]) {
@@ -569,6 +607,9 @@ import ExternalAccessory
569
607
  default: modifierIndent = " "
570
608
  }
571
609
  }
610
+ if let bold = modifier["bold"] as? Bool {
611
+ modifierBold = bold
612
+ }
572
613
  }
573
614
 
574
615
  for item in kitchen {
@@ -598,25 +639,30 @@ import ExternalAccessory
598
639
 
599
640
  // Print modifiers
600
641
  if let modifiers = item["modifiers"] as? [[String: Any]] {
601
- // Apply modifier size if specified
642
+ // Apply modifier formatting (size and bold)
602
643
  if modifierSizeCode != 0x00 {
603
644
  printData.append(Data([0x1D, 0x21, modifierSizeCode]))
604
645
  }
646
+ if modifierBold {
647
+ printData.append(Data([0x1B, 0x45, 0x01])) // Bold on
648
+ }
605
649
 
606
650
  for mod in modifiers {
607
651
  if let modName = getString(mod["name"]) {
608
652
  var prefix = ""
609
653
 
610
654
  // Determine prefix based on style
655
+ // Note: Using ASCII-compatible characters only
656
+ // Unicode characters like • and → display as garbled text on thermal printers
611
657
  switch modifierStyle.lowercased() {
612
658
  case "dash":
613
659
  prefix = "-"
614
660
  case "bullet":
615
- prefix = ""
661
+ prefix = "*" // ASCII asterisk instead of Unicode bullet
616
662
  case "arrow":
617
- prefix = ""
663
+ prefix = ">" // ASCII greater-than instead of Unicode arrow
618
664
  default:
619
- prefix = ""
665
+ prefix = "-" // Default to dash for best compatibility
620
666
  }
621
667
 
622
668
  // Build modifier line
@@ -628,17 +674,18 @@ import ExternalAccessory
628
674
  }
629
675
  }
630
676
 
631
- // Reset modifier size if it was changed
677
+ // Reset modifier formatting (size and bold)
678
+ if modifierBold {
679
+ printData.append(Data([0x1B, 0x45, 0x00])) // Bold off
680
+ }
632
681
  if modifierSizeCode != 0x00 {
633
- printData.append(Data([0x1D, 0x21, 0x00]))
682
+ printData.append(Data([0x1D, 0x21, itemSizeCode])) // Reset to item size, not 0x00
634
683
  }
635
684
  }
636
685
  }
637
686
 
638
687
  // Reset item formatting
639
- if itemSizeCode != 0x00 {
640
- printData.append(Data([0x1D, 0x21, 0x00])) // Normal size
641
- }
688
+ printData.append(Data([0x1D, 0x21, 0x00])) // Always reset to normal size
642
689
  if itemBold {
643
690
  printData.append(Data([0x1B, 0x45, 0x00])) // Bold off
644
691
  }
@@ -647,7 +694,7 @@ import ExternalAccessory
647
694
 
648
695
  // ========== TOTAL SECTION ==========
649
696
  // Separator before total (normal font size)
650
- if let separatorData = generateSeparator().data(using: .utf8) {
697
+ if let separatorData = generateSeparator(withChar: separatorChar).data(using: .utf8) {
651
698
  printData.append(separatorData)
652
699
  }
653
700
 
@@ -675,7 +722,7 @@ import ExternalAccessory
675
722
  // ========== TOTAL SECTION (NEW STRUCTURE) ==========
676
723
  if let total = getString(template["total"]) {
677
724
  // Separator (may need to account for total font size)
678
- if let separatorData = generateSeparator().data(using: .utf8) {
725
+ if let separatorData = generateSeparator(withChar: separatorChar).data(using: .utf8) {
679
726
  printData.append(separatorData)
680
727
  }
681
728
 
@@ -713,19 +760,20 @@ import ExternalAccessory
713
760
  }
714
761
  }
715
762
 
716
- // Separator (normal font size)
717
- if let separatorData = generateSeparator().data(using: .utf8) {
718
- printData.append(separatorData)
719
- }
720
-
721
- // Payment method
722
- if let paymentMethod = getString(template["paymentMethod"]),
723
- let paymentData = ("PAYMENT BY:\(paymentMethod)\t\(getString(template["total"]) ?? "")\n").data(using: .utf8) {
724
- printData.append(paymentData)
763
+ // Payment method (with separator only if payment method exists)
764
+ if let paymentMethod = getString(template["paymentMethod"]) {
765
+ // Separator before payment
766
+ if let separatorData = generateSeparator(withChar: separatorChar).data(using: .utf8) {
767
+ printData.append(separatorData)
768
+ }
769
+
770
+ if let paymentData = ("PAYMENT BY:\(paymentMethod)\t\(getString(template["total"]) ?? "")\n").data(using: .utf8) {
771
+ printData.append(paymentData)
772
+ }
725
773
  }
726
774
 
727
- // Separator (normal font size)
728
- if let separatorData = generateSeparator().data(using: .utf8) {
775
+ // Single separator before footer
776
+ if let separatorData = generateSeparator(withChar: separatorChar).data(using: .utf8) {
729
777
  printData.append(separatorData)
730
778
  }
731
779
 
@@ -761,10 +809,21 @@ import ExternalAccessory
761
809
  printData.append(messageData)
762
810
  }
763
811
 
764
- // Reset footer formatting
812
+ // Reset footer formatting before timestamp
765
813
  if footerSizeCode != 0x00 {
766
814
  printData.append(Data([0x1D, 0x21, 0x00])) // Normal size
767
815
  }
816
+ if footerBold {
817
+ printData.append(Data([0x1B, 0x45, 0x00])) // Bold off
818
+ }
819
+
820
+ // Print timestamp
821
+ let dateFormat = getString(footer["date_format"]) ?? "YYYY-MM-DD"
822
+ let timeFormat = getString(footer["time_format"]) ?? "24H"
823
+ let timestamp = formatTimestamp(dateFormat: dateFormat, timeFormat: timeFormat)
824
+ if let timestampData = (timestamp + "\n").data(using: .utf8) {
825
+ printData.append(timestampData)
826
+ }
768
827
  }
769
828
 
770
829
  // Line feeds
@@ -834,6 +893,48 @@ import ExternalAccessory
834
893
  return String(describing: value)
835
894
  }
836
895
 
896
+ /**
897
+ * Formats the current timestamp based on date and time format preferences
898
+ * - Parameters:
899
+ * - dateFormat: Format string like "YYYY-MM-DD", "DD-MM-YYYY", "MM-DD-YYYY"
900
+ * - timeFormat: "12H" or "24H"
901
+ * - Returns: Formatted timestamp string
902
+ */
903
+ private func formatTimestamp(dateFormat: String, timeFormat: String) -> String {
904
+ let now = Date()
905
+ let calendar = Calendar.current
906
+
907
+ let year = calendar.component(.year, from: now)
908
+ let month = calendar.component(.month, from: now)
909
+ let day = calendar.component(.day, from: now)
910
+ let hour = calendar.component(.hour, from: now)
911
+ let minute = calendar.component(.minute, from: now)
912
+ let second = calendar.component(.second, from: now)
913
+
914
+ // Format date
915
+ var formattedDate: String
916
+ switch dateFormat {
917
+ case "DD-MM-YYYY", "DD/MM/YYYY":
918
+ formattedDate = String(format: "%02d-%02d-%04d", day, month, year)
919
+ case "MM-DD-YYYY", "MM/DD/YYYY":
920
+ formattedDate = String(format: "%02d-%02d-%04d", month, day, year)
921
+ default: // "YYYY-MM-DD"
922
+ formattedDate = String(format: "%04d-%02d-%02d", year, month, day)
923
+ }
924
+
925
+ // Format time
926
+ var formattedTime: String
927
+ if timeFormat == "12H" {
928
+ let hour12 = hour % 12 == 0 ? 12 : hour % 12
929
+ let ampm = hour >= 12 ? "PM" : "AM"
930
+ formattedTime = String(format: "%d:%02d:%02d %@", hour12, minute, second, ampm)
931
+ } else { // 24H
932
+ formattedTime = String(format: "%02d:%02d:%02d", hour, minute, second)
933
+ }
934
+
935
+ return "\(formattedDate) \(formattedTime)"
936
+ }
937
+
837
938
  /**
838
939
  * Generates a separator line based on current printer width and font magnification
839
940
  * - Parameter sizeCode: The current font size code (0x00, 0x11, 0x22, 0x33)
@@ -845,7 +946,7 @@ import ExternalAccessory
845
946
  * - 0x22 (3x3): 3x width (one-third as many characters fit)
846
947
  * - 0x33 (4x4): 4x width (one-quarter as many characters fit)
847
948
  */
848
- private func generateSeparator(forSizeCode sizeCode: UInt8 = 0x00) -> String {
949
+ private func generateSeparator(forSizeCode sizeCode: UInt8 = 0x00, withChar separatorChar: String? = nil) -> String {
849
950
  // Calculate width divisor based on font magnification
850
951
  let widthMultiplier: Int
851
952
  switch sizeCode {
@@ -858,8 +959,11 @@ import ExternalAccessory
858
959
  // Calculate actual character count that fits on the line
859
960
  let effectiveWidth = printerWidth / widthMultiplier
860
961
 
962
+ // Use provided separator character or fall back to default
963
+ let charToUse = separatorChar ?? defaultSeparatorChar
964
+
861
965
  // Generate separator string
862
- return String(repeating: separatorChar, count: effectiveWidth) + "\n"
966
+ return String(repeating: charToUse, count: effectiveWidth) + "\n"
863
967
  }
864
968
 
865
969
  // MARK: - Callback Storage
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "seven365-zyprinter",
3
- "version": "1.0.9",
3
+ "version": "1.1.0",
4
4
  "description": "Capacitor plugin for Zywell/Zyprint thermal printer integration with Bluetooth and WiFi support",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",