zebra-day 0.0.37__py3-none-any.whl → 1.0.2__py3-none-any.whl

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 (181) hide show
  1. zebra_day/__init__.py +35 -0
  2. zebra_day/bin/__init__.py +0 -0
  3. zebra_day/bin/fetch_zebra_config.py +15 -0
  4. zebra_day/bin/generate_coord_grid_zpl.py +50 -0
  5. zebra_day/bin/print_zpl_from_file.py +21 -0
  6. zebra_day/bin/probe_new_label_dimensions.py +75 -0
  7. zebra_day/bin/scan_for_networed_zebra_printers_arp_scan.sh +1 -0
  8. zebra_day/bin/scan_for_networed_zebra_printers_curl.sh +30 -0
  9. zebra_day/bin/zserve.py +701 -259
  10. zebra_day/cli/__init__.py +240 -0
  11. zebra_day/cli/cognito.py +121 -0
  12. zebra_day/cli/gui.py +255 -0
  13. zebra_day/cli/printer.py +168 -0
  14. zebra_day/cli/template.py +176 -0
  15. zebra_day/cmd_mgr.py +35 -0
  16. zebra_day/etc/Monoid-Regular-HalfTight-Dollar-0-1-l.ttf +0 -0
  17. zebra_day/etc/label_styles/blank.zpl +0 -0
  18. zebra_day/etc/label_styles/cornersStripOf4Squares_1inX1in.zpl +55 -0
  19. zebra_day/etc/label_styles/corners_1inX2in.zpl +28 -0
  20. zebra_day/etc/label_styles/corners_20cmX30cm.zpl +6 -0
  21. zebra_day/etc/label_styles/corners_smallTube.zpl +7 -0
  22. zebra_day/etc/label_styles/corners_unspecifiedDimensions.zpl +15 -0
  23. zebra_day/etc/label_styles/generic_2inX1in.zpl +21 -0
  24. zebra_day/etc/label_styles/plate_1inX0.25in.zpl +9 -0
  25. zebra_day/etc/label_styles/plate_1inX0.25inHD.zpl +9 -0
  26. zebra_day/etc/label_styles/smallTubeWdotHD_prod.zpl +8 -0
  27. zebra_day/etc/label_styles/smallTubeWdot_corners.zpl +7 -0
  28. zebra_day/etc/label_styles/smallTubeWdot_prod.zpl +8 -0
  29. zebra_day/etc/label_styles/smallTubeWdot_prodAlt1.zpl +6 -0
  30. zebra_day/etc/label_styles/smallTubeWdot_prodAlt1b.zpl +3 -0
  31. zebra_day/etc/label_styles/smallTubeWdot_prodV2.zpl +8 -0
  32. zebra_day/etc/label_styles/smallTubeWdot_reagent.zpl +29 -0
  33. zebra_day/etc/label_styles/stripOf4Squares_1inX1in.zpl +32 -0
  34. zebra_day/etc/label_styles/test_800dX800dCoordinateArray.zpl +1 -0
  35. zebra_day/etc/label_styles/tmps/.hold +0 -0
  36. zebra_day/etc/label_styles/tmps/tmp_zpl_templates.here +0 -0
  37. zebra_day/etc/label_styles/tube_20mmX30mmA.zpl +7 -0
  38. zebra_day/etc/label_styles/tube_2inX0.3in.zpl +15 -0
  39. zebra_day/etc/label_styles/tube_2inX0.5in.zpl +15 -0
  40. zebra_day/etc/label_styles/tube_2inX0.5inHD.zpl +15 -0
  41. zebra_day/etc/label_styles/tube_2inX1in.zpl +25 -0
  42. zebra_day/etc/label_styles/tube_2inX1inHD.zpl +22 -0
  43. zebra_day/etc/label_styles/tube_2inX1inHDv3.zpl +21 -0
  44. zebra_day/etc/old_printer_config/.hold +0 -0
  45. zebra_day/etc/old_printer_config/2026-02-01_01:50:25.022846_printer_config.json +1 -0
  46. zebra_day/etc/old_printer_config/2026-02-01_01:50:25.033657_printer_config.json +1 -0
  47. zebra_day/etc/old_printer_config/2026-02-01_01:50:25.039597_printer_config.json +3 -0
  48. zebra_day/etc/old_printer_config/2026-02-01_01:50:25.047295_printer_config.json +1 -0
  49. zebra_day/etc/old_printer_config/2026-02-01_01:50:25.055804_printer_config.json +1 -0
  50. zebra_day/etc/old_printer_config/2026-02-01_01:50:25.061337_printer_config.json +3 -0
  51. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.073326_printer_config.json +1 -0
  52. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.081950_printer_config.json +1 -0
  53. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.088251_printer_config.json +3 -0
  54. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.096501_printer_config.json +1 -0
  55. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.104767_printer_config.json +1 -0
  56. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.110364_printer_config.json +3 -0
  57. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.118239_printer_config.json +1 -0
  58. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.125950_printer_config.json +1 -0
  59. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.349866_printer_config.json +1 -0
  60. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.361085_printer_config.json +3 -0
  61. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.558323_printer_config.json +1 -0
  62. zebra_day/etc/old_printer_config/2026-02-01_01:51:24.565756_printer_config.json +3 -0
  63. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.739070_printer_config.json +16 -0
  64. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.753796_printer_config.json +1 -0
  65. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.760201_printer_config.json +3 -0
  66. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.768747_printer_config.json +1 -0
  67. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.775312_printer_config.json +3 -0
  68. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.782533_printer_config.json +1 -0
  69. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.789287_printer_config.json +1 -0
  70. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.794230_printer_config.json +3 -0
  71. zebra_day/etc/old_printer_config/2026-02-01_01:51:29.800021_printer_config.json +5 -0
  72. zebra_day/etc/printer_config.json +16 -0
  73. zebra_day/etc/printer_config.template.json +16 -0
  74. zebra_day/etc/tmp_printers0.json +5 -0
  75. zebra_day/etc/tmp_printers374.json +5 -0
  76. zebra_day/etc/tmp_printers383.json +5 -0
  77. zebra_day/etc/tmp_printers450.json +5 -0
  78. zebra_day/etc/tmp_printers504.json +5 -0
  79. zebra_day/etc/tmp_printers608.json +5 -0
  80. zebra_day/etc/tmp_printers657.json +5 -0
  81. zebra_day/etc/tmp_printers838.json +5 -0
  82. zebra_day/etc/tmp_printers839.json +5 -0
  83. zebra_day/etc/tmp_printers933.json +5 -0
  84. zebra_day/etc/tmp_printers957.json +5 -0
  85. zebra_day/exceptions.py +88 -0
  86. zebra_day/files/.hold +0 -0
  87. zebra_day/files/hold +0 -0
  88. zebra_day/files/test_png_17696.png +0 -0
  89. zebra_day/files/test_png_23477.png +0 -0
  90. zebra_day/files/test_png_28157.png +0 -0
  91. zebra_day/files/test_png_35832.png +0 -0
  92. zebra_day/files/test_png_36400.png +0 -0
  93. zebra_day/files/test_png_40816.png +0 -0
  94. zebra_day/files/test_png_49564.png +0 -0
  95. zebra_day/files/test_png_53848.png +0 -0
  96. zebra_day/files/test_png_62542.png +0 -0
  97. zebra_day/files/test_png_91597.png +0 -0
  98. zebra_day/files/test_png_93633.png +0 -0
  99. zebra_day/files/tmpbjo3k7q1.png +0 -0
  100. zebra_day/files/tmpigtr4pwy.png +0 -0
  101. zebra_day/files/zpl_label_tube_2inX1in_2026-02-01_01:51:24.370964.png +0 -0
  102. zebra_day/logging_config.py +74 -0
  103. zebra_day/logs/.hold +0 -0
  104. zebra_day/logs/print_requests.log +2 -0
  105. zebra_day/paths.py +143 -0
  106. zebra_day/print_mgr.py +487 -103
  107. zebra_day/static/datschund.css +140 -0
  108. zebra_day/static/datschund.png +0 -0
  109. zebra_day/static/daylily.png +0 -0
  110. zebra_day/static/favicon.svg +20 -0
  111. zebra_day/static/general.css +99 -0
  112. zebra_day/static/js/zebra_modern.js +172 -0
  113. zebra_day/static/lsmc.css +354 -0
  114. zebra_day/static/moon.jpeg +0 -0
  115. zebra_day/static/oakland.css +197 -0
  116. zebra_day/static/petrichor.css +150 -0
  117. zebra_day/static/popday_daylily.css +140 -0
  118. zebra_day/static/style.css +183 -0
  119. zebra_day/static/triangles.css +122 -0
  120. zebra_day/static/tron.css +277 -0
  121. zebra_day/static/zebra_modern.css +771 -0
  122. zebra_day/static/zebras.css +176 -0
  123. zebra_day/templates/base.html +36 -0
  124. zebra_day/templates/bpr.html +72 -0
  125. zebra_day/templates/build_new_config.html +36 -0
  126. zebra_day/templates/build_print_request.html +32 -0
  127. zebra_day/templates/chg_ui_style.html +19 -0
  128. zebra_day/templates/edit_template.html +128 -0
  129. zebra_day/templates/edit_zpl.html +37 -0
  130. zebra_day/templates/index.html +82 -0
  131. zebra_day/templates/legacy/base.html +37 -0
  132. zebra_day/templates/legacy/bpr.html +72 -0
  133. zebra_day/templates/legacy/build_new_config.html +36 -0
  134. zebra_day/templates/legacy/build_print_request.html +32 -0
  135. zebra_day/templates/legacy/chg_ui_style.html +19 -0
  136. zebra_day/templates/legacy/edit_template.html +128 -0
  137. zebra_day/templates/legacy/edit_zpl.html +37 -0
  138. zebra_day/templates/legacy/index.html +82 -0
  139. zebra_day/templates/legacy/list_prior_configs.html +24 -0
  140. zebra_day/templates/legacy/print_result.html +30 -0
  141. zebra_day/templates/legacy/printer_details.html +25 -0
  142. zebra_day/templates/legacy/printer_status.html +70 -0
  143. zebra_day/templates/legacy/save_result.html +17 -0
  144. zebra_day/templates/legacy/send_print_request.html +34 -0
  145. zebra_day/templates/legacy/simple_print.html +94 -0
  146. zebra_day/templates/legacy/view_pstation_json.html +29 -0
  147. zebra_day/templates/list_prior_configs.html +24 -0
  148. zebra_day/templates/modern/base.html +98 -0
  149. zebra_day/templates/modern/config.html +141 -0
  150. zebra_day/templates/modern/dashboard.html +160 -0
  151. zebra_day/templates/modern/print_request.html +141 -0
  152. zebra_day/templates/modern/print_result.html +88 -0
  153. zebra_day/templates/modern/printer_detail.html +117 -0
  154. zebra_day/templates/modern/printers.html +133 -0
  155. zebra_day/templates/modern/save_result.html +46 -0
  156. zebra_day/templates/modern/template_editor.html +172 -0
  157. zebra_day/templates/modern/templates.html +122 -0
  158. zebra_day/templates/print_result.html +30 -0
  159. zebra_day/templates/printer_details.html +25 -0
  160. zebra_day/templates/printer_status.html +70 -0
  161. zebra_day/templates/save_result.html +17 -0
  162. zebra_day/templates/send_print_request.html +34 -0
  163. zebra_day/templates/simple_print.html +94 -0
  164. zebra_day/templates/view_pstation_json.html +29 -0
  165. zebra_day/web/__init__.py +9 -0
  166. zebra_day/web/app.py +171 -0
  167. zebra_day/web/auth.py +172 -0
  168. zebra_day/web/middleware.py +159 -0
  169. zebra_day/web/routers/__init__.py +2 -0
  170. zebra_day/web/routers/api.py +163 -0
  171. zebra_day/web/routers/ui.py +1051 -0
  172. zebra_day/zpl_renderer.py +273 -0
  173. zebra_day-1.0.2.dist-info/METADATA +786 -0
  174. zebra_day-1.0.2.dist-info/RECORD +179 -0
  175. {zebra_day-0.0.37.dist-info → zebra_day-1.0.2.dist-info}/WHEEL +1 -1
  176. zebra_day-1.0.2.dist-info/entry_points.txt +4 -0
  177. zebra_day/bin/te.py +0 -905
  178. zebra_day-0.0.37.dist-info/METADATA +0 -1177
  179. zebra_day-0.0.37.dist-info/RECORD +0 -10
  180. {zebra_day-0.0.37.dist-info → zebra_day-1.0.2.dist-info/licenses}/LICENSE +0 -0
  181. {zebra_day-0.0.37.dist-info → zebra_day-1.0.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,273 @@
1
+ """
2
+ Local ZPL-to-PNG renderer using Pillow and zint-bindings.
3
+
4
+ Replaces external Labelary API dependency with offline rendering.
5
+ Supports the ZPL commands used in zebra_day templates:
6
+ - ^XA/^XZ: Label start/end
7
+ - ^FO: Field origin (positioning)
8
+ - ^FD/^FS: Field data/separator
9
+ - ^A0N/^ADN: Font selection
10
+ - ^BY: Barcode field default
11
+ - ^B3N: Code 39 barcode
12
+ - ^BCN: Code 128 barcode
13
+ - ^BQN: QR code
14
+ - ^BXN: Data Matrix
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import io
19
+ import logging
20
+ import re
21
+ from dataclasses import dataclass, field
22
+ from pathlib import Path
23
+
24
+ from PIL import Image, ImageDraw, ImageFont
25
+
26
+ try:
27
+ import zint
28
+ ZINT_AVAILABLE = True
29
+ except ImportError:
30
+ ZINT_AVAILABLE = False
31
+
32
+ _log = logging.getLogger(__name__)
33
+
34
+ # Default label dimensions for 4x6 inch label at 8 dpmm (203 dpi)
35
+ DEFAULT_LABEL_WIDTH_DOTS = 812 # 4 inches * 203 dpi
36
+ DEFAULT_LABEL_HEIGHT_DOTS = 1218 # 6 inches * 203 dpi
37
+
38
+ # Barcode type mappings
39
+ BARCODE_TYPES = {
40
+ 'B3N': 'CODE39', # Code 39
41
+ 'BCN': 'CODE128', # Code 128
42
+ 'BQN': 'QRCODE', # QR Code
43
+ 'BXN': 'DATAMATRIX', # Data Matrix
44
+ }
45
+
46
+
47
+ @dataclass
48
+ class FontSpec:
49
+ """Font specification from ZPL ^A command."""
50
+ height: int = 30
51
+ width: int = 20
52
+
53
+
54
+ @dataclass
55
+ class BarcodeSpec:
56
+ """Barcode specification from ZPL ^BY command."""
57
+ module_width: int = 2
58
+ ratio: float = 3.0
59
+ height: int = 10
60
+
61
+
62
+ @dataclass
63
+ class RenderState:
64
+ """Current rendering state while parsing ZPL."""
65
+ x: int = 0
66
+ y: int = 0
67
+ font: FontSpec = field(default_factory=FontSpec)
68
+ barcode: BarcodeSpec = field(default_factory=BarcodeSpec)
69
+ current_barcode_type: str | None = None
70
+ barcode_height: int = 40
71
+
72
+
73
+ def _get_font(height: int) -> ImageFont.FreeTypeFont | ImageFont.ImageFont:
74
+ """Get a font at the specified height. Falls back to default if unavailable."""
75
+ try:
76
+ # Try common monospace fonts
77
+ for font_name in ['DejaVuSansMono.ttf', 'Menlo.ttc', 'Courier New.ttf', 'monospace']:
78
+ try:
79
+ return ImageFont.truetype(font_name, height)
80
+ except OSError:
81
+ continue
82
+ # Fallback to default
83
+ return ImageFont.load_default()
84
+ except Exception:
85
+ return ImageFont.load_default()
86
+
87
+
88
+ def _render_barcode(barcode_type: str, data: str, height: int = 40, module_width: int = 2) -> Image.Image | None:
89
+ """Render a barcode using zint-bindings."""
90
+ if not ZINT_AVAILABLE:
91
+ _log.warning("zint-bindings not available, cannot render barcode")
92
+ return None
93
+
94
+ import tempfile
95
+ import os
96
+
97
+ try:
98
+ symbol = zint.Symbol()
99
+
100
+ # Map barcode type to zint symbology
101
+ if barcode_type == 'CODE39':
102
+ symbol.symbology = zint.Symbology.CODE39
103
+ elif barcode_type == 'CODE128':
104
+ symbol.symbology = zint.Symbology.CODE128
105
+ elif barcode_type == 'QRCODE':
106
+ symbol.symbology = zint.Symbology.QRCODE
107
+ elif barcode_type == 'DATAMATRIX':
108
+ symbol.symbology = zint.Symbology.DATAMATRIX
109
+ else:
110
+ _log.warning("Unknown barcode type: %s", barcode_type)
111
+ return None
112
+
113
+ symbol.height = height
114
+ symbol.scale = max(1, module_width)
115
+ symbol.show_text = False # ZPL typically handles text separately
116
+
117
+ # Create temp file for output
118
+ with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as f:
119
+ temp_path = f.name
120
+
121
+ symbol.outfile = temp_path
122
+ symbol.encode(data)
123
+ symbol.print() # Write to outfile
124
+
125
+ # Load the image
126
+ if os.path.exists(temp_path):
127
+ img = Image.open(temp_path).convert('RGBA')
128
+ os.unlink(temp_path) # Clean up temp file
129
+ return img
130
+ return None
131
+ except Exception as e:
132
+ _log.warning("Failed to render barcode: %s", e)
133
+ return None
134
+
135
+
136
+ def _parse_font_command(cmd: str) -> FontSpec:
137
+ """Parse ^A0N or ^ADN font command."""
138
+ # ^A0N,height,width or ^ADN,height,width
139
+ parts = cmd.split(',')
140
+ height = int(parts[1]) if len(parts) > 1 and parts[1].strip() else 30
141
+ width = int(parts[2]) if len(parts) > 2 and parts[2].strip() else height // 2
142
+ return FontSpec(height=height, width=width)
143
+
144
+
145
+ def _parse_barcode_default(cmd: str) -> BarcodeSpec:
146
+ """Parse ^BY command for barcode defaults."""
147
+ # ^BY[module_width],[ratio],[height]
148
+ parts = cmd.split(',')
149
+ module_width = int(parts[0]) if parts[0].strip() else 2
150
+ ratio = float(parts[1]) if len(parts) > 1 and parts[1].strip() else 3.0
151
+ height = int(parts[2]) if len(parts) > 2 and parts[2].strip() else 10
152
+ return BarcodeSpec(module_width=module_width, ratio=ratio, height=height)
153
+
154
+
155
+ def _parse_position(cmd: str) -> tuple[int, int]:
156
+ """Parse ^FO command for field origin."""
157
+ # ^FOx,y
158
+ parts = cmd.split(',')
159
+ x = int(parts[0]) if parts[0].strip() else 0
160
+ y = int(parts[1]) if len(parts) > 1 and parts[1].strip() else 0
161
+ return x, y
162
+
163
+
164
+ def _parse_barcode_command(cmd: str) -> int:
165
+ """Parse barcode command (^B3N, ^BCN, etc.) and return height."""
166
+ # Format: ^B3N,orientation,height,... or ^BCN,orientation,height,...
167
+ parts = cmd.split(',')
168
+ # Height is usually the 3rd parameter (index 2) for most barcode commands
169
+ if len(parts) > 2 and parts[2].strip():
170
+ try:
171
+ return int(parts[2])
172
+ except ValueError:
173
+ pass
174
+ return 40 # Default height
175
+
176
+
177
+ def render_zpl_to_png(
178
+ zpl_string: str,
179
+ output_path: str | Path,
180
+ width: int = DEFAULT_LABEL_WIDTH_DOTS,
181
+ height: int = DEFAULT_LABEL_HEIGHT_DOTS,
182
+ ) -> str:
183
+ """
184
+ Render ZPL string to PNG image.
185
+
186
+ Args:
187
+ zpl_string: ZPL format string to render
188
+ output_path: Path to save the PNG file
189
+ width: Label width in dots (default: 812 for 4" at 203 dpi)
190
+ height: Label height in dots (default: 1218 for 6" at 203 dpi)
191
+
192
+ Returns:
193
+ Path to the saved PNG file
194
+ """
195
+ output_path = Path(output_path)
196
+ output_path.parent.mkdir(parents=True, exist_ok=True)
197
+
198
+ # Create white background image
199
+ img = Image.new('RGB', (width, height), 'white')
200
+ draw = ImageDraw.Draw(img)
201
+
202
+ state = RenderState()
203
+
204
+ # Parse ZPL commands - split on ^ character
205
+ commands = re.split(r'\^', zpl_string)
206
+
207
+ pending_text: str | None = None
208
+
209
+ for cmd in commands:
210
+ cmd = cmd.strip()
211
+ if not cmd:
212
+ continue
213
+
214
+ # Label start/end - ignore
215
+ if cmd.startswith('XA') or cmd.startswith('XZ'):
216
+ continue
217
+
218
+ # Field origin - set position
219
+ if cmd.startswith('FO'):
220
+ state.x, state.y = _parse_position(cmd[2:])
221
+ continue
222
+
223
+ # Font commands
224
+ if cmd.startswith('A0N') or cmd.startswith('ADN'):
225
+ state.font = _parse_font_command(cmd)
226
+ continue
227
+
228
+ # Barcode default
229
+ if cmd.startswith('BY'):
230
+ state.barcode = _parse_barcode_default(cmd[2:])
231
+ continue
232
+
233
+ # Barcode type commands
234
+ for bc_cmd, bc_type in BARCODE_TYPES.items():
235
+ if cmd.startswith(bc_cmd):
236
+ state.current_barcode_type = bc_type
237
+ state.barcode_height = _parse_barcode_command(cmd)
238
+ break
239
+ else:
240
+ # Not a barcode command, check for field data
241
+ if cmd.startswith('FD'):
242
+ # Extract data between FD and FS
243
+ data = cmd[2:]
244
+ if data.endswith('FS'):
245
+ data = data[:-2]
246
+
247
+ # If there's a pending barcode type, render barcode
248
+ if state.current_barcode_type:
249
+ bc_img = _render_barcode(
250
+ state.current_barcode_type,
251
+ data,
252
+ height=state.barcode_height,
253
+ module_width=state.barcode.module_width,
254
+ )
255
+ if bc_img:
256
+ img.paste(bc_img, (state.x, state.y), bc_img if bc_img.mode == 'RGBA' else None)
257
+ state.current_barcode_type = None
258
+ else:
259
+ # Render text
260
+ font = _get_font(state.font.height)
261
+ draw.text((state.x, state.y), data, fill='black', font=font)
262
+ continue
263
+
264
+ # Field separator - just a marker, usually handled with FD
265
+ if cmd.startswith('FS'):
266
+ continue
267
+
268
+ # Save the image
269
+ img.save(str(output_path), 'PNG')
270
+ _log.info("Label image saved as %s", output_path)
271
+
272
+ return str(output_path)
273
+