labfreed 0.2.8__py3-none-any.whl → 0.2.9__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.

Potentially problematic release.


This version of labfreed might be problematic. Click here for more details.

Files changed (44) hide show
  1. labfreed/__init__.py +11 -11
  2. labfreed/labfreed_infrastructure.py +258 -258
  3. labfreed/pac_cat/__init__.py +19 -19
  4. labfreed/pac_cat/category_base.py +51 -51
  5. labfreed/pac_cat/pac_cat.py +150 -150
  6. labfreed/pac_cat/predefined_categories.py +200 -200
  7. labfreed/pac_id/__init__.py +19 -19
  8. labfreed/pac_id/extension.py +48 -48
  9. labfreed/pac_id/id_segment.py +89 -89
  10. labfreed/pac_id/pac_id.py +140 -140
  11. labfreed/pac_id/url_parser.py +155 -155
  12. labfreed/pac_id/url_serializer.py +85 -84
  13. labfreed/pac_id_resolver/__init__.py +2 -2
  14. labfreed/pac_id_resolver/cit_common.py +81 -81
  15. labfreed/pac_id_resolver/cit_v1.py +244 -244
  16. labfreed/pac_id_resolver/cit_v2.py +313 -313
  17. labfreed/pac_id_resolver/resolver.py +97 -97
  18. labfreed/pac_id_resolver/services.py +82 -82
  19. labfreed/qr/__init__.py +1 -1
  20. labfreed/qr/generate_qr.py +422 -422
  21. labfreed/trex/__init__.py +16 -16
  22. labfreed/trex/python_convenience/__init__.py +3 -3
  23. labfreed/trex/python_convenience/data_table.py +87 -87
  24. labfreed/trex/python_convenience/pyTREX.py +248 -248
  25. labfreed/trex/python_convenience/quantity.py +66 -66
  26. labfreed/trex/table_segment.py +245 -245
  27. labfreed/trex/trex.py +69 -69
  28. labfreed/trex/trex_base_models.py +209 -209
  29. labfreed/trex/value_segments.py +99 -99
  30. labfreed/utilities/base36.py +82 -82
  31. labfreed/well_known_extensions/__init__.py +4 -4
  32. labfreed/well_known_extensions/default_extension_interpreters.py +6 -6
  33. labfreed/well_known_extensions/display_name_extension.py +40 -40
  34. labfreed/well_known_extensions/trex_extension.py +30 -30
  35. labfreed/well_known_keys/gs1/__init__.py +5 -5
  36. labfreed/well_known_keys/gs1/gs1.py +3 -3
  37. labfreed/well_known_keys/labfreed/well_known_keys.py +15 -15
  38. labfreed/well_known_keys/unece/__init__.py +3 -3
  39. labfreed/well_known_keys/unece/unece_units.py +67 -67
  40. {labfreed-0.2.8.dist-info → labfreed-0.2.9.dist-info}/METADATA +11 -8
  41. labfreed-0.2.9.dist-info/RECORD +45 -0
  42. {labfreed-0.2.8.dist-info → labfreed-0.2.9.dist-info}/licenses/LICENSE +21 -21
  43. labfreed-0.2.8.dist-info/RECORD +0 -45
  44. {labfreed-0.2.8.dist-info → labfreed-0.2.9.dist-info}/WHEEL +0 -0
@@ -1,422 +1,422 @@
1
- #!/usr/bin/env python
2
- import typer
3
- from rich import print
4
- from typing_extensions import Annotated
5
- import numpy as np
6
- import segno
7
- from segno import DataOverflowError, writers
8
- from typing import List
9
- from enum import Enum
10
-
11
-
12
-
13
- class Direction(str, Enum):
14
- LEFT_TO_RIGHT = "LTR"
15
- TOP_TO_BOTTOM = "TTB"
16
- RIGHT_TO_LEFT = "RTL"
17
-
18
- # 5x5 block of bits that can be used to place decoration next to barcode.
19
- class VisualMarker:
20
- size = 5
21
-
22
- def __init__(self, bits):
23
- # true means 1 bit, false means 0 bit (eg: 1 means black, 0 means white)
24
- self.bits = bits
25
-
26
- marker_dict = {
27
- 'A': bytearray([
28
- 0x00, 0x01, 0x01, 0x01, 0x00, # preformatted
29
- 0x01, 0x00, 0x00, 0x00, 0x01,
30
- 0x01, 0x01, 0x01, 0x01, 0x01,
31
- 0x01, 0x00, 0x00, 0x00, 0x01,
32
- 0x01, 0x00, 0x00, 0x00, 0x01,
33
- ]),
34
- 'B': bytearray([
35
- 0x01, 0x01, 0x01, 0x01, 0x00, # preformatted
36
- 0x01, 0x00, 0x00, 0x00, 0x01,
37
- 0x01, 0x01, 0x01, 0x01, 0x00,
38
- 0x01, 0x00, 0x00, 0x00, 0x01,
39
- 0x01, 0x01, 0x01, 0x01, 0x00,
40
- ]),
41
- 'C': bytearray([
42
- 0x00, 0x01, 0x01, 0x01, 0x01, # preformatted
43
- 0x01, 0x00, 0x00, 0x00, 0x00,
44
- 0x01, 0x00, 0x00, 0x00, 0x00,
45
- 0x01, 0x00, 0x00, 0x00, 0x00,
46
- 0x00, 0x01, 0x01, 0x01, 0x01,
47
- ]),
48
- 'D': bytearray([
49
- 0x01, 0x01, 0x01, 0x01, 0x00, # preformatted
50
- 0x01, 0x00, 0x00, 0x00, 0x01,
51
- 0x01, 0x00, 0x00, 0x00, 0x01,
52
- 0x01, 0x00, 0x00, 0x00, 0x01,
53
- 0x01, 0x01, 0x01, 0x01, 0x00,
54
- ]),
55
- 'E': bytearray([
56
- 0x01, 0x01, 0x01, 0x01, 0x01, # preformatted
57
- 0x01, 0x00, 0x00, 0x00, 0x00,
58
- 0x01, 0x01, 0x01, 0x01, 0x01,
59
- 0x01, 0x00, 0x00, 0x00, 0x00,
60
- 0x01, 0x01, 0x01, 0x01, 0x01,
61
- ]),
62
- 'F': bytearray([
63
- 0x01, 0x01, 0x01, 0x01, 0x01, # preformatted
64
- 0x01, 0x00, 0x00, 0x00, 0x00,
65
- 0x01, 0x01, 0x01, 0x01, 0x01,
66
- 0x01, 0x00, 0x00, 0x00, 0x00,
67
- 0x01, 0x00, 0x00, 0x00, 0x00,
68
- ]),
69
- 'G': bytearray([
70
- 0x00, 0x01, 0x01, 0x01, 0x01, # preformatted
71
- 0x01, 0x00, 0x00, 0x00, 0x00,
72
- 0x01, 0x00, 0x01, 0x01, 0x00,
73
- 0x01, 0x00, 0x00, 0x00, 0x01,
74
- 0x00, 0x01, 0x01, 0x01, 0x01,
75
- ]),
76
- 'H': bytearray([
77
- 0x01, 0x00, 0x00, 0x00, 0x01, # preformatted
78
- 0x01, 0x00, 0x00, 0x00, 0x01,
79
- 0x01, 0x01, 0x01, 0x01, 0x01,
80
- 0x01, 0x00, 0x00, 0x00, 0x01,
81
- 0x01, 0x00, 0x00, 0x00, 0x01,
82
- ]),
83
- 'I': bytearray([
84
- 0x00, 0x00, 0x01, 0x00, 0x00, # preformatted
85
- 0x00, 0x00, 0x01, 0x00, 0x00,
86
- 0x00, 0x00, 0x01, 0x00, 0x00,
87
- 0x00, 0x00, 0x01, 0x00, 0x00,
88
- 0x00, 0x00, 0x01, 0x00, 0x00,
89
- ]),
90
- 'J': bytearray([
91
- 0x00, 0x00, 0x01, 0x00, 0x00, # preformatted
92
- 0x00, 0x00, 0x01, 0x00, 0x00,
93
- 0x00, 0x00, 0x01, 0x00, 0x00,
94
- 0x00, 0x00, 0x01, 0x00, 0x00,
95
- 0x00, 0x01, 0x00, 0x00, 0x00,
96
- ]),
97
- 'K': bytearray([
98
- 0x01, 0x00, 0x00, 0x00, 0x01, # preformatted
99
- 0x01, 0x00, 0x00, 0x01, 0x00,
100
- 0x01, 0x01, 0x01, 0x00, 0x00,
101
- 0x01, 0x00, 0x00, 0x01, 0x00,
102
- 0x01, 0x00, 0x00, 0x00, 0x01,
103
- ]),
104
- 'L': bytearray([
105
- 0x01, 0x00, 0x00, 0x00, 0x00, # preformatted
106
- 0x01, 0x00, 0x00, 0x00, 0x00,
107
- 0x01, 0x00, 0x00, 0x00, 0x00,
108
- 0x01, 0x00, 0x00, 0x00, 0x00,
109
- 0x01, 0x01, 0x01, 0x01, 0x01,
110
- ]),
111
- 'M': bytearray([
112
- 0x01, 0x01, 0x00, 0x01, 0x01, # preformatted
113
- 0x01, 0x00, 0x01, 0x00, 0x01,
114
- 0x01, 0x00, 0x00, 0x00, 0x01,
115
- 0x01, 0x00, 0x00, 0x00, 0x01,
116
- 0x01, 0x00, 0x00, 0x00, 0x01,
117
- ]),
118
- 'N': bytearray([
119
- 0x01, 0x00, 0x00, 0x00, 0x01, # preformatted
120
- 0x01, 0x01, 0x00, 0x00, 0x01,
121
- 0x01, 0x00, 0x01, 0x00, 0x01,
122
- 0x01, 0x00, 0x00, 0x01, 0x01,
123
- 0x01, 0x00, 0x00, 0x00, 0x01,
124
- ]),
125
- 'O': bytearray([
126
- 0x00, 0x01, 0x01, 0x01, 0x00, # preformatted
127
- 0x01, 0x00, 0x00, 0x00, 0x01,
128
- 0x01, 0x00, 0x00, 0x00, 0x01,
129
- 0x01, 0x00, 0x00, 0x00, 0x01,
130
- 0x00, 0x01, 0x01, 0x01, 0x00,
131
- ]),
132
- 'P': bytearray([
133
- 0x01, 0x01, 0x01, 0x01, 0x00, # preformatted
134
- 0x01, 0x00, 0x00, 0x00, 0x01,
135
- 0x01, 0x01, 0x01, 0x01, 0x00,
136
- 0x01, 0x00, 0x00, 0x00, 0x00,
137
- 0x01, 0x00, 0x00, 0x00, 0x00,
138
- ]),
139
- 'Q': bytearray([
140
- 0x00, 0x01, 0x01, 0x01, 0x00, # preformatted
141
- 0x01, 0x00, 0x00, 0x00, 0x01,
142
- 0x01, 0x00, 0x01, 0x00, 0x01,
143
- 0x01, 0x00, 0x00, 0x01, 0x00,
144
- 0x00, 0x01, 0x01, 0x00, 0x01,
145
- ]),
146
- 'R': bytearray([
147
- 0x01, 0x01, 0x01, 0x01, 0x00, # preformatted
148
- 0x01, 0x00, 0x00, 0x01, 0x00,
149
- 0x01, 0x01, 0x01, 0x00, 0x00,
150
- 0x01, 0x00, 0x00, 0x01, 0x00,
151
- 0x01, 0x00, 0x00, 0x00, 0x01,
152
- ]),
153
- 'S': bytearray([
154
- 0x00, 0x01, 0x01, 0x01, 0x01, # preformatted
155
- 0x01, 0x00, 0x00, 0x00, 0x00,
156
- 0x00, 0x01, 0x01, 0x01, 0x00,
157
- 0x00, 0x00, 0x00, 0x00, 0x01,
158
- 0x01, 0x01, 0x01, 0x01, 0x00,
159
- ]),
160
- 'T': bytearray([
161
- 0x01, 0x01, 0x01, 0x01, 0x01, # preformatted
162
- 0x00, 0x00, 0x01, 0x00, 0x00,
163
- 0x00, 0x00, 0x01, 0x00, 0x00,
164
- 0x00, 0x00, 0x01, 0x00, 0x00,
165
- 0x00, 0x00, 0x01, 0x00, 0x00,
166
- ]),
167
- 'U': bytearray([
168
- 0x01, 0x00, 0x00, 0x00, 0x01, # preformatted
169
- 0x01, 0x00, 0x00, 0x00, 0x01,
170
- 0x01, 0x00, 0x00, 0x00, 0x01,
171
- 0x01, 0x00, 0x00, 0x00, 0x01,
172
- 0x00, 0x01, 0x01, 0x01, 0x00,
173
- ]),
174
- 'V': bytearray([
175
- 0x01, 0x00, 0x00, 0x00, 0x01, # preformatted
176
- 0x01, 0x00, 0x00, 0x00, 0x01,
177
- 0x00, 0x01, 0x00, 0x01, 0x00,
178
- 0x00, 0x01, 0x01, 0x01, 0x00,
179
- 0x00, 0x00, 0x01, 0x00, 0x00,
180
- ]),
181
- 'W': bytearray([
182
- 0x01, 0x00, 0x00, 0x00, 0x01, # preformatted
183
- 0x01, 0x00, 0x01, 0x00, 0x01,
184
- 0x01, 0x00, 0x01, 0x00, 0x01,
185
- 0x00, 0x01, 0x01, 0x01, 0x00,
186
- 0x00, 0x01, 0x00, 0x01, 0x00,
187
- ]),
188
- 'X': bytearray([
189
- 0x01, 0x00, 0x00, 0x00, 0x01, # preformatted
190
- 0x00, 0x01, 0x00, 0x01, 0x00,
191
- 0x00, 0x00, 0x01, 0x00, 0x00,
192
- 0x00, 0x01, 0x00, 0x01, 0x00,
193
- 0x01, 0x00, 0x00, 0x00, 0x01,
194
- ]),
195
- 'Y': bytearray([
196
- 0x01, 0x00, 0x00, 0x00, 0x01, # preformatted
197
- 0x01, 0x00, 0x00, 0x00, 0x01,
198
- 0x00, 0x01, 0x01, 0x01, 0x00,
199
- 0x00, 0x00, 0x01, 0x00, 0x00,
200
- 0x00, 0x00, 0x01, 0x00, 0x00,
201
- ]),
202
- 'Z': bytearray([
203
- 0x01, 0x01, 0x01, 0x01, 0x01, # preformatted
204
- 0x00, 0x00, 0x00, 0x01, 0x00,
205
- 0x00, 0x00, 0x01, 0x00, 0x00,
206
- 0x00, 0x01, 0x00, 0x00, 0x00,
207
- 0x01, 0x01, 0x01, 0x01, 0x01,
208
- ]),
209
- '0': bytearray([
210
- 0x00, 0x00, 0x01, 0x00, 0x00, # preformatted
211
- 0x00, 0x01, 0x00, 0x01, 0x00,
212
- 0x00, 0x01, 0x00, 0x01, 0x00,
213
- 0x00, 0x01, 0x00, 0x01, 0x00,
214
- 0x00, 0x00, 0x01, 0x00, 0x00,
215
- ]),
216
- '1': bytearray([
217
- 0x00, 0x00, 0x01, 0x00, 0x00, # preformatted
218
- 0x00, 0x01, 0x01, 0x00, 0x00,
219
- 0x01, 0x00, 0x01, 0x00, 0x00,
220
- 0x00, 0x00, 0x01, 0x00, 0x00,
221
- 0x00, 0x00, 0x01, 0x00, 0x00,
222
- ]),
223
- '2': bytearray([
224
- 0x00, 0x01, 0x01, 0x01, 0x00, # preformatted
225
- 0x01, 0x00, 0x00, 0x00, 0x01,
226
- 0x00, 0x00, 0x01, 0x01, 0x00,
227
- 0x00, 0x01, 0x00, 0x00, 0x00,
228
- 0x01, 0x01, 0x01, 0x01, 0x01,
229
- ]),
230
- '3': bytearray([
231
- 0x01, 0x01, 0x01, 0x01, 0x00, # preformatted
232
- 0x00, 0x00, 0x00, 0x00, 0x01,
233
- 0x01, 0x01, 0x01, 0x01, 0x00,
234
- 0x00, 0x00, 0x00, 0x00, 0x01,
235
- 0x01, 0x01, 0x01, 0x01, 0x00,
236
- ]),
237
- '4': bytearray([
238
- 0x01, 0x00, 0x00, 0x00, 0x00, # preformatted
239
- 0x01, 0x00, 0x00, 0x01, 0x00,
240
- 0x01, 0x01, 0x01, 0x01, 0x01,
241
- 0x00, 0x00, 0x00, 0x01, 0x00,
242
- 0x00, 0x00, 0x00, 0x01, 0x00,
243
- ]),
244
- '5': bytearray([
245
- 0x01, 0x01, 0x01, 0x01, 0x01, # preformatted
246
- 0x01, 0x00, 0x00, 0x00, 0x00,
247
- 0x01, 0x01, 0x01, 0x01, 0x00,
248
- 0x00, 0x00, 0x00, 0x00, 0x01,
249
- 0x01, 0x01, 0x01, 0x01, 0x00,
250
- ]),
251
- '6': bytearray([
252
- 0x00, 0x01, 0x01, 0x01, 0x00, # preformatted
253
- 0x01, 0x00, 0x00, 0x00, 0x00,
254
- 0x01, 0x01, 0x01, 0x01, 0x00,
255
- 0x01, 0x00, 0x00, 0x00, 0x01,
256
- 0x00, 0x01, 0x01, 0x01, 0x00,
257
- ]),
258
- '7': bytearray([
259
- 0x01, 0x01, 0x01, 0x01, 0x01, # preformatted
260
- 0x00, 0x00, 0x00, 0x01, 0x00,
261
- 0x00, 0x00, 0x01, 0x00, 0x00,
262
- 0x00, 0x01, 0x00, 0x00, 0x00,
263
- 0x01, 0x00, 0x00, 0x00, 0x00,
264
- ]),
265
- '8': bytearray([
266
- 0x00, 0x01, 0x01, 0x01, 0x00, # preformatted
267
- 0x01, 0x00, 0x00, 0x00, 0x01,
268
- 0x00, 0x01, 0x01, 0x01, 0x00,
269
- 0x01, 0x00, 0x00, 0x00, 0x01,
270
- 0x00, 0x01, 0x01, 0x01, 0x00,
271
- ]),
272
- '9': bytearray([
273
- 0x00, 0x01, 0x01, 0x01, 0x00, # preformatted
274
- 0x01, 0x00, 0x00, 0x00, 0x01,
275
- 0x00, 0x01, 0x01, 0x01, 0x01,
276
- 0x00, 0x00, 0x00, 0x00, 0x01,
277
- 0x00, 0x01, 0x01, 0x01, 0x00,
278
- ]),
279
- '.': bytearray([
280
- 0x00, 0x00, 0x00, 0x00, 0x00, # preformatted
281
- 0x00, 0x00, 0x00, 0x00, 0x00,
282
- 0x00, 0x00, 0x01, 0x00, 0x00,
283
- 0x00, 0x00, 0x00, 0x00, 0x00,
284
- 0x00, 0x00, 0x00, 0x00, 0x00,
285
- ]),
286
- ',': bytearray([
287
- 0x00, 0x00, 0x00, 0x00, 0x00, # preformatted
288
- 0x00, 0x00, 0x00, 0x00, 0x00,
289
- 0x00, 0x00, 0x00, 0x00, 0x00,
290
- 0x00, 0x00, 0x01, 0x00, 0x00,
291
- 0x00, 0x00, 0x01, 0x00, 0x00,
292
- ]),
293
- ':': bytearray([
294
- 0x00, 0x00, 0x00, 0x00, 0x00, # preformatted
295
- 0x00, 0x00, 0x01, 0x00, 0x00,
296
- 0x00, 0x00, 0x00, 0x00, 0x00,
297
- 0x00, 0x00, 0x00, 0x00, 0x00,
298
- 0x00, 0x00, 0x01, 0x00, 0x00,
299
- ]),
300
- ';': bytearray([
301
- 0x00, 0x00, 0x00, 0x00, 0x00, # preformatted
302
- 0x00, 0x00, 0x01, 0x00, 0x00,
303
- 0x00, 0x00, 0x00, 0x00, 0x00,
304
- 0x00, 0x00, 0x01, 0x00, 0x00,
305
- 0x00, 0x00, 0x01, 0x00, 0x00,
306
- ]),
307
- '-': bytearray([
308
- 0x00, 0x00, 0x00, 0x00, 0x00, # preformatted
309
- 0x00, 0x00, 0x00, 0x00, 0x00,
310
- 0x00, 0x01, 0x01, 0x01, 0x00,
311
- 0x00, 0x00, 0x00, 0x00, 0x00,
312
- 0x00, 0x00, 0x00, 0x00, 0x00,
313
- ])
314
- }
315
-
316
- def generate_from_text(chars: str, direction:Direction, size_in_modules: int):
317
- # fill up with "."
318
- squares_cnt = (size_in_modules + 1) // (VisualMarker.size+1)
319
- leftover = (size_in_modules + 1) % (VisualMarker.size+1)
320
- if len(chars) < squares_cnt:
321
- chars = chars + '.' * (squares_cnt - len(chars))
322
- result: List[bytearray] = []
323
- for char in chars[:squares_cnt]:
324
- if char in VisualMarker.marker_dict:
325
- marker = VisualMarker.marker_dict[char]
326
- else:
327
- marker = VisualMarker.marker_dict['.']
328
- result.append(marker)
329
- return VisualMarker.generate_from_squares(result, direction, leftover)
330
-
331
- def generate_from_squares(marker_squares: List[bytearray], direction:Direction, padding_at_end: int):
332
- # spacer is a column of zeros if RTL/LTR, or a row if TTB/BTT
333
- axis = 0 if direction == Direction.TOP_TO_BOTTOM else 1
334
- spacer_dim = (1, VisualMarker.size) if axis==0 else (VisualMarker.size, 1)
335
- spacer = np.zeros(spacer_dim, dtype=np.uint8)
336
- result = np.array([])
337
- for marker in marker_squares:
338
- if len(marker) != VisualMarker.size*VisualMarker.size:
339
- raise ValueError("All markers must be 5x5")
340
-
341
- np_marker = np.array(marker).reshape(VisualMarker.size, VisualMarker.size)
342
- if result.size == 0:
343
- # the first item doesn't need a spacer
344
- result = np_marker
345
- else:
346
- # append in direction specified
347
- if direction == Direction.RIGHT_TO_LEFT:
348
- result = np.concatenate((np_marker, spacer, result), axis=1)
349
- else:
350
- result = np.concatenate((result, spacer, np_marker), axis=axis)
351
- if(padding_at_end > 0):
352
- padding_dim = (padding_at_end, VisualMarker.size) if axis==0 else (VisualMarker.size, padding_at_end)
353
- padding= np.zeros(padding_dim, dtype=np.uint8)
354
-
355
- # append in direction specified
356
- if direction == Direction.RIGHT_TO_LEFT:
357
- result = np.concatenate((padding, result), axis=1)
358
- else:
359
- result = np.concatenate((result, padding), axis=axis)
360
- return result
361
-
362
-
363
- app = typer.Typer()
364
-
365
-
366
- def _generate_qr_with_markers(qr_str, text, title, direction):
367
- if title:
368
- #try to use standard size 10. Go bigger if 10 does not fit the data
369
- try:
370
- qr = segno.make_qr(qr_str, error="L", version=10)
371
- except DataOverflowError:
372
- qr = segno.make_qr(qr_str, error="L")
373
- else:
374
- qr = segno.make_qr(qr_str, error="L")
375
-
376
- if(qr.mode != "alphanumeric"):
377
- print("[bold yellow]Large QR:[/bold yellow] Provided URL is not alphanumeric!")
378
- block_count = len(qr.matrix)
379
- print(f"[bold]Size:[/bold] {block_count}")
380
- print(f"[bold]Version:[/bold] {qr.version}")
381
- print(f"[bold]Error Level:[/bold] {qr.error}")
382
-
383
-
384
- qr_matrix = np.array(qr.matrix)
385
- visual_marker = VisualMarker.generate_from_text(text.upper(), direction, block_count)
386
- if title:
387
- title_marker = VisualMarker.generate_from_text(title.upper(), direction, block_count)
388
-
389
- append_axis = 1 if direction == Direction.TOP_TO_BOTTOM else 0
390
- padding_dim = (4, block_count) if append_axis==0 else (block_count, qr.default_border_size)
391
- padding= np.zeros(padding_dim, dtype=np.uint8)
392
- if title:
393
- combined_matrix = np.concatenate((title_marker, padding, qr_matrix, padding, visual_marker), axis=append_axis)
394
- else:
395
- combined_matrix = np.concatenate((qr_matrix, padding, visual_marker), axis=append_axis)
396
-
397
- return combined_matrix
398
-
399
- def save_qr_with_markers(url, text="PAC", title=None, direction = Direction.LEFT_TO_RIGHT, fmt='png', path='qr'):
400
- combined_matrix = _generate_qr_with_markers(url, text="PAC", title=None, direction = Direction.LEFT_TO_RIGHT)
401
- outfile = f'{path}.{fmt}'
402
- match fmt:
403
- case 'png':
404
- writers.write_png(combined_matrix, combined_matrix.shape[::-1], out=outfile, border=9)
405
- case 'svg':
406
- writers.write_svg(combined_matrix, combined_matrix.shape[::-1], out=outfile, border=9)
407
-
408
-
409
-
410
-
411
- def main(url: Annotated[str, typer.Argument(help="The PAC-ID to be rendered as QR.")],
412
- outfile: Annotated[typer.FileBinaryWrite, typer.Option(help="The file the qr will be written to.")] = "qr.svg",
413
- text: Annotated[str, typer.Option(help="The text of the PAC decoration.")] = "PAC",
414
- direction: Annotated[Direction, typer.Option(help="The position/direction of the PAC decoration.")] = Direction.TOP_TO_BOTTOM):
415
-
416
- save_qr_with_markers(url, text=text, direction=direction, path=outfile)
417
-
418
-
419
-
420
- if __name__ == "__main__":
421
- typer.run(main)
422
-
1
+ #!/usr/bin/env python
2
+ import typer
3
+ from rich import print
4
+ from typing_extensions import Annotated
5
+ import numpy as np
6
+ import segno
7
+ from segno import DataOverflowError, writers
8
+ from typing import List
9
+ from enum import Enum
10
+
11
+
12
+
13
+ class Direction(str, Enum):
14
+ LEFT_TO_RIGHT = "LTR"
15
+ TOP_TO_BOTTOM = "TTB"
16
+ RIGHT_TO_LEFT = "RTL"
17
+
18
+ # 5x5 block of bits that can be used to place decoration next to barcode.
19
+ class VisualMarker:
20
+ size = 5
21
+
22
+ def __init__(self, bits):
23
+ # true means 1 bit, false means 0 bit (eg: 1 means black, 0 means white)
24
+ self.bits = bits
25
+
26
+ marker_dict = {
27
+ 'A': bytearray([
28
+ 0x00, 0x01, 0x01, 0x01, 0x00, # preformatted
29
+ 0x01, 0x00, 0x00, 0x00, 0x01,
30
+ 0x01, 0x01, 0x01, 0x01, 0x01,
31
+ 0x01, 0x00, 0x00, 0x00, 0x01,
32
+ 0x01, 0x00, 0x00, 0x00, 0x01,
33
+ ]),
34
+ 'B': bytearray([
35
+ 0x01, 0x01, 0x01, 0x01, 0x00, # preformatted
36
+ 0x01, 0x00, 0x00, 0x00, 0x01,
37
+ 0x01, 0x01, 0x01, 0x01, 0x00,
38
+ 0x01, 0x00, 0x00, 0x00, 0x01,
39
+ 0x01, 0x01, 0x01, 0x01, 0x00,
40
+ ]),
41
+ 'C': bytearray([
42
+ 0x00, 0x01, 0x01, 0x01, 0x01, # preformatted
43
+ 0x01, 0x00, 0x00, 0x00, 0x00,
44
+ 0x01, 0x00, 0x00, 0x00, 0x00,
45
+ 0x01, 0x00, 0x00, 0x00, 0x00,
46
+ 0x00, 0x01, 0x01, 0x01, 0x01,
47
+ ]),
48
+ 'D': bytearray([
49
+ 0x01, 0x01, 0x01, 0x01, 0x00, # preformatted
50
+ 0x01, 0x00, 0x00, 0x00, 0x01,
51
+ 0x01, 0x00, 0x00, 0x00, 0x01,
52
+ 0x01, 0x00, 0x00, 0x00, 0x01,
53
+ 0x01, 0x01, 0x01, 0x01, 0x00,
54
+ ]),
55
+ 'E': bytearray([
56
+ 0x01, 0x01, 0x01, 0x01, 0x01, # preformatted
57
+ 0x01, 0x00, 0x00, 0x00, 0x00,
58
+ 0x01, 0x01, 0x01, 0x01, 0x01,
59
+ 0x01, 0x00, 0x00, 0x00, 0x00,
60
+ 0x01, 0x01, 0x01, 0x01, 0x01,
61
+ ]),
62
+ 'F': bytearray([
63
+ 0x01, 0x01, 0x01, 0x01, 0x01, # preformatted
64
+ 0x01, 0x00, 0x00, 0x00, 0x00,
65
+ 0x01, 0x01, 0x01, 0x01, 0x01,
66
+ 0x01, 0x00, 0x00, 0x00, 0x00,
67
+ 0x01, 0x00, 0x00, 0x00, 0x00,
68
+ ]),
69
+ 'G': bytearray([
70
+ 0x00, 0x01, 0x01, 0x01, 0x01, # preformatted
71
+ 0x01, 0x00, 0x00, 0x00, 0x00,
72
+ 0x01, 0x00, 0x01, 0x01, 0x00,
73
+ 0x01, 0x00, 0x00, 0x00, 0x01,
74
+ 0x00, 0x01, 0x01, 0x01, 0x01,
75
+ ]),
76
+ 'H': bytearray([
77
+ 0x01, 0x00, 0x00, 0x00, 0x01, # preformatted
78
+ 0x01, 0x00, 0x00, 0x00, 0x01,
79
+ 0x01, 0x01, 0x01, 0x01, 0x01,
80
+ 0x01, 0x00, 0x00, 0x00, 0x01,
81
+ 0x01, 0x00, 0x00, 0x00, 0x01,
82
+ ]),
83
+ 'I': bytearray([
84
+ 0x00, 0x00, 0x01, 0x00, 0x00, # preformatted
85
+ 0x00, 0x00, 0x01, 0x00, 0x00,
86
+ 0x00, 0x00, 0x01, 0x00, 0x00,
87
+ 0x00, 0x00, 0x01, 0x00, 0x00,
88
+ 0x00, 0x00, 0x01, 0x00, 0x00,
89
+ ]),
90
+ 'J': bytearray([
91
+ 0x00, 0x00, 0x01, 0x00, 0x00, # preformatted
92
+ 0x00, 0x00, 0x01, 0x00, 0x00,
93
+ 0x00, 0x00, 0x01, 0x00, 0x00,
94
+ 0x00, 0x00, 0x01, 0x00, 0x00,
95
+ 0x00, 0x01, 0x00, 0x00, 0x00,
96
+ ]),
97
+ 'K': bytearray([
98
+ 0x01, 0x00, 0x00, 0x00, 0x01, # preformatted
99
+ 0x01, 0x00, 0x00, 0x01, 0x00,
100
+ 0x01, 0x01, 0x01, 0x00, 0x00,
101
+ 0x01, 0x00, 0x00, 0x01, 0x00,
102
+ 0x01, 0x00, 0x00, 0x00, 0x01,
103
+ ]),
104
+ 'L': bytearray([
105
+ 0x01, 0x00, 0x00, 0x00, 0x00, # preformatted
106
+ 0x01, 0x00, 0x00, 0x00, 0x00,
107
+ 0x01, 0x00, 0x00, 0x00, 0x00,
108
+ 0x01, 0x00, 0x00, 0x00, 0x00,
109
+ 0x01, 0x01, 0x01, 0x01, 0x01,
110
+ ]),
111
+ 'M': bytearray([
112
+ 0x01, 0x01, 0x00, 0x01, 0x01, # preformatted
113
+ 0x01, 0x00, 0x01, 0x00, 0x01,
114
+ 0x01, 0x00, 0x00, 0x00, 0x01,
115
+ 0x01, 0x00, 0x00, 0x00, 0x01,
116
+ 0x01, 0x00, 0x00, 0x00, 0x01,
117
+ ]),
118
+ 'N': bytearray([
119
+ 0x01, 0x00, 0x00, 0x00, 0x01, # preformatted
120
+ 0x01, 0x01, 0x00, 0x00, 0x01,
121
+ 0x01, 0x00, 0x01, 0x00, 0x01,
122
+ 0x01, 0x00, 0x00, 0x01, 0x01,
123
+ 0x01, 0x00, 0x00, 0x00, 0x01,
124
+ ]),
125
+ 'O': bytearray([
126
+ 0x00, 0x01, 0x01, 0x01, 0x00, # preformatted
127
+ 0x01, 0x00, 0x00, 0x00, 0x01,
128
+ 0x01, 0x00, 0x00, 0x00, 0x01,
129
+ 0x01, 0x00, 0x00, 0x00, 0x01,
130
+ 0x00, 0x01, 0x01, 0x01, 0x00,
131
+ ]),
132
+ 'P': bytearray([
133
+ 0x01, 0x01, 0x01, 0x01, 0x00, # preformatted
134
+ 0x01, 0x00, 0x00, 0x00, 0x01,
135
+ 0x01, 0x01, 0x01, 0x01, 0x00,
136
+ 0x01, 0x00, 0x00, 0x00, 0x00,
137
+ 0x01, 0x00, 0x00, 0x00, 0x00,
138
+ ]),
139
+ 'Q': bytearray([
140
+ 0x00, 0x01, 0x01, 0x01, 0x00, # preformatted
141
+ 0x01, 0x00, 0x00, 0x00, 0x01,
142
+ 0x01, 0x00, 0x01, 0x00, 0x01,
143
+ 0x01, 0x00, 0x00, 0x01, 0x00,
144
+ 0x00, 0x01, 0x01, 0x00, 0x01,
145
+ ]),
146
+ 'R': bytearray([
147
+ 0x01, 0x01, 0x01, 0x01, 0x00, # preformatted
148
+ 0x01, 0x00, 0x00, 0x01, 0x00,
149
+ 0x01, 0x01, 0x01, 0x00, 0x00,
150
+ 0x01, 0x00, 0x00, 0x01, 0x00,
151
+ 0x01, 0x00, 0x00, 0x00, 0x01,
152
+ ]),
153
+ 'S': bytearray([
154
+ 0x00, 0x01, 0x01, 0x01, 0x01, # preformatted
155
+ 0x01, 0x00, 0x00, 0x00, 0x00,
156
+ 0x00, 0x01, 0x01, 0x01, 0x00,
157
+ 0x00, 0x00, 0x00, 0x00, 0x01,
158
+ 0x01, 0x01, 0x01, 0x01, 0x00,
159
+ ]),
160
+ 'T': bytearray([
161
+ 0x01, 0x01, 0x01, 0x01, 0x01, # preformatted
162
+ 0x00, 0x00, 0x01, 0x00, 0x00,
163
+ 0x00, 0x00, 0x01, 0x00, 0x00,
164
+ 0x00, 0x00, 0x01, 0x00, 0x00,
165
+ 0x00, 0x00, 0x01, 0x00, 0x00,
166
+ ]),
167
+ 'U': bytearray([
168
+ 0x01, 0x00, 0x00, 0x00, 0x01, # preformatted
169
+ 0x01, 0x00, 0x00, 0x00, 0x01,
170
+ 0x01, 0x00, 0x00, 0x00, 0x01,
171
+ 0x01, 0x00, 0x00, 0x00, 0x01,
172
+ 0x00, 0x01, 0x01, 0x01, 0x00,
173
+ ]),
174
+ 'V': bytearray([
175
+ 0x01, 0x00, 0x00, 0x00, 0x01, # preformatted
176
+ 0x01, 0x00, 0x00, 0x00, 0x01,
177
+ 0x00, 0x01, 0x00, 0x01, 0x00,
178
+ 0x00, 0x01, 0x01, 0x01, 0x00,
179
+ 0x00, 0x00, 0x01, 0x00, 0x00,
180
+ ]),
181
+ 'W': bytearray([
182
+ 0x01, 0x00, 0x00, 0x00, 0x01, # preformatted
183
+ 0x01, 0x00, 0x01, 0x00, 0x01,
184
+ 0x01, 0x00, 0x01, 0x00, 0x01,
185
+ 0x00, 0x01, 0x01, 0x01, 0x00,
186
+ 0x00, 0x01, 0x00, 0x01, 0x00,
187
+ ]),
188
+ 'X': bytearray([
189
+ 0x01, 0x00, 0x00, 0x00, 0x01, # preformatted
190
+ 0x00, 0x01, 0x00, 0x01, 0x00,
191
+ 0x00, 0x00, 0x01, 0x00, 0x00,
192
+ 0x00, 0x01, 0x00, 0x01, 0x00,
193
+ 0x01, 0x00, 0x00, 0x00, 0x01,
194
+ ]),
195
+ 'Y': bytearray([
196
+ 0x01, 0x00, 0x00, 0x00, 0x01, # preformatted
197
+ 0x01, 0x00, 0x00, 0x00, 0x01,
198
+ 0x00, 0x01, 0x01, 0x01, 0x00,
199
+ 0x00, 0x00, 0x01, 0x00, 0x00,
200
+ 0x00, 0x00, 0x01, 0x00, 0x00,
201
+ ]),
202
+ 'Z': bytearray([
203
+ 0x01, 0x01, 0x01, 0x01, 0x01, # preformatted
204
+ 0x00, 0x00, 0x00, 0x01, 0x00,
205
+ 0x00, 0x00, 0x01, 0x00, 0x00,
206
+ 0x00, 0x01, 0x00, 0x00, 0x00,
207
+ 0x01, 0x01, 0x01, 0x01, 0x01,
208
+ ]),
209
+ '0': bytearray([
210
+ 0x00, 0x00, 0x01, 0x00, 0x00, # preformatted
211
+ 0x00, 0x01, 0x00, 0x01, 0x00,
212
+ 0x00, 0x01, 0x00, 0x01, 0x00,
213
+ 0x00, 0x01, 0x00, 0x01, 0x00,
214
+ 0x00, 0x00, 0x01, 0x00, 0x00,
215
+ ]),
216
+ '1': bytearray([
217
+ 0x00, 0x00, 0x01, 0x00, 0x00, # preformatted
218
+ 0x00, 0x01, 0x01, 0x00, 0x00,
219
+ 0x01, 0x00, 0x01, 0x00, 0x00,
220
+ 0x00, 0x00, 0x01, 0x00, 0x00,
221
+ 0x00, 0x00, 0x01, 0x00, 0x00,
222
+ ]),
223
+ '2': bytearray([
224
+ 0x00, 0x01, 0x01, 0x01, 0x00, # preformatted
225
+ 0x01, 0x00, 0x00, 0x00, 0x01,
226
+ 0x00, 0x00, 0x01, 0x01, 0x00,
227
+ 0x00, 0x01, 0x00, 0x00, 0x00,
228
+ 0x01, 0x01, 0x01, 0x01, 0x01,
229
+ ]),
230
+ '3': bytearray([
231
+ 0x01, 0x01, 0x01, 0x01, 0x00, # preformatted
232
+ 0x00, 0x00, 0x00, 0x00, 0x01,
233
+ 0x01, 0x01, 0x01, 0x01, 0x00,
234
+ 0x00, 0x00, 0x00, 0x00, 0x01,
235
+ 0x01, 0x01, 0x01, 0x01, 0x00,
236
+ ]),
237
+ '4': bytearray([
238
+ 0x01, 0x00, 0x00, 0x00, 0x00, # preformatted
239
+ 0x01, 0x00, 0x00, 0x01, 0x00,
240
+ 0x01, 0x01, 0x01, 0x01, 0x01,
241
+ 0x00, 0x00, 0x00, 0x01, 0x00,
242
+ 0x00, 0x00, 0x00, 0x01, 0x00,
243
+ ]),
244
+ '5': bytearray([
245
+ 0x01, 0x01, 0x01, 0x01, 0x01, # preformatted
246
+ 0x01, 0x00, 0x00, 0x00, 0x00,
247
+ 0x01, 0x01, 0x01, 0x01, 0x00,
248
+ 0x00, 0x00, 0x00, 0x00, 0x01,
249
+ 0x01, 0x01, 0x01, 0x01, 0x00,
250
+ ]),
251
+ '6': bytearray([
252
+ 0x00, 0x01, 0x01, 0x01, 0x00, # preformatted
253
+ 0x01, 0x00, 0x00, 0x00, 0x00,
254
+ 0x01, 0x01, 0x01, 0x01, 0x00,
255
+ 0x01, 0x00, 0x00, 0x00, 0x01,
256
+ 0x00, 0x01, 0x01, 0x01, 0x00,
257
+ ]),
258
+ '7': bytearray([
259
+ 0x01, 0x01, 0x01, 0x01, 0x01, # preformatted
260
+ 0x00, 0x00, 0x00, 0x01, 0x00,
261
+ 0x00, 0x00, 0x01, 0x00, 0x00,
262
+ 0x00, 0x01, 0x00, 0x00, 0x00,
263
+ 0x01, 0x00, 0x00, 0x00, 0x00,
264
+ ]),
265
+ '8': bytearray([
266
+ 0x00, 0x01, 0x01, 0x01, 0x00, # preformatted
267
+ 0x01, 0x00, 0x00, 0x00, 0x01,
268
+ 0x00, 0x01, 0x01, 0x01, 0x00,
269
+ 0x01, 0x00, 0x00, 0x00, 0x01,
270
+ 0x00, 0x01, 0x01, 0x01, 0x00,
271
+ ]),
272
+ '9': bytearray([
273
+ 0x00, 0x01, 0x01, 0x01, 0x00, # preformatted
274
+ 0x01, 0x00, 0x00, 0x00, 0x01,
275
+ 0x00, 0x01, 0x01, 0x01, 0x01,
276
+ 0x00, 0x00, 0x00, 0x00, 0x01,
277
+ 0x00, 0x01, 0x01, 0x01, 0x00,
278
+ ]),
279
+ '.': bytearray([
280
+ 0x00, 0x00, 0x00, 0x00, 0x00, # preformatted
281
+ 0x00, 0x00, 0x00, 0x00, 0x00,
282
+ 0x00, 0x00, 0x01, 0x00, 0x00,
283
+ 0x00, 0x00, 0x00, 0x00, 0x00,
284
+ 0x00, 0x00, 0x00, 0x00, 0x00,
285
+ ]),
286
+ ',': bytearray([
287
+ 0x00, 0x00, 0x00, 0x00, 0x00, # preformatted
288
+ 0x00, 0x00, 0x00, 0x00, 0x00,
289
+ 0x00, 0x00, 0x00, 0x00, 0x00,
290
+ 0x00, 0x00, 0x01, 0x00, 0x00,
291
+ 0x00, 0x00, 0x01, 0x00, 0x00,
292
+ ]),
293
+ ':': bytearray([
294
+ 0x00, 0x00, 0x00, 0x00, 0x00, # preformatted
295
+ 0x00, 0x00, 0x01, 0x00, 0x00,
296
+ 0x00, 0x00, 0x00, 0x00, 0x00,
297
+ 0x00, 0x00, 0x00, 0x00, 0x00,
298
+ 0x00, 0x00, 0x01, 0x00, 0x00,
299
+ ]),
300
+ ';': bytearray([
301
+ 0x00, 0x00, 0x00, 0x00, 0x00, # preformatted
302
+ 0x00, 0x00, 0x01, 0x00, 0x00,
303
+ 0x00, 0x00, 0x00, 0x00, 0x00,
304
+ 0x00, 0x00, 0x01, 0x00, 0x00,
305
+ 0x00, 0x00, 0x01, 0x00, 0x00,
306
+ ]),
307
+ '-': bytearray([
308
+ 0x00, 0x00, 0x00, 0x00, 0x00, # preformatted
309
+ 0x00, 0x00, 0x00, 0x00, 0x00,
310
+ 0x00, 0x01, 0x01, 0x01, 0x00,
311
+ 0x00, 0x00, 0x00, 0x00, 0x00,
312
+ 0x00, 0x00, 0x00, 0x00, 0x00,
313
+ ])
314
+ }
315
+
316
+ def generate_from_text(chars: str, direction:Direction, size_in_modules: int):
317
+ # fill up with "."
318
+ squares_cnt = (size_in_modules + 1) // (VisualMarker.size+1)
319
+ leftover = (size_in_modules + 1) % (VisualMarker.size+1)
320
+ if len(chars) < squares_cnt:
321
+ chars = chars + '.' * (squares_cnt - len(chars))
322
+ result: List[bytearray] = []
323
+ for char in chars[:squares_cnt]:
324
+ if char in VisualMarker.marker_dict:
325
+ marker = VisualMarker.marker_dict[char]
326
+ else:
327
+ marker = VisualMarker.marker_dict['.']
328
+ result.append(marker)
329
+ return VisualMarker.generate_from_squares(result, direction, leftover)
330
+
331
+ def generate_from_squares(marker_squares: List[bytearray], direction:Direction, padding_at_end: int):
332
+ # spacer is a column of zeros if RTL/LTR, or a row if TTB/BTT
333
+ axis = 0 if direction == Direction.TOP_TO_BOTTOM else 1
334
+ spacer_dim = (1, VisualMarker.size) if axis==0 else (VisualMarker.size, 1)
335
+ spacer = np.zeros(spacer_dim, dtype=np.uint8)
336
+ result = np.array([])
337
+ for marker in marker_squares:
338
+ if len(marker) != VisualMarker.size*VisualMarker.size:
339
+ raise ValueError("All markers must be 5x5")
340
+
341
+ np_marker = np.array(marker).reshape(VisualMarker.size, VisualMarker.size)
342
+ if result.size == 0:
343
+ # the first item doesn't need a spacer
344
+ result = np_marker
345
+ else:
346
+ # append in direction specified
347
+ if direction == Direction.RIGHT_TO_LEFT:
348
+ result = np.concatenate((np_marker, spacer, result), axis=1)
349
+ else:
350
+ result = np.concatenate((result, spacer, np_marker), axis=axis)
351
+ if(padding_at_end > 0):
352
+ padding_dim = (padding_at_end, VisualMarker.size) if axis==0 else (VisualMarker.size, padding_at_end)
353
+ padding= np.zeros(padding_dim, dtype=np.uint8)
354
+
355
+ # append in direction specified
356
+ if direction == Direction.RIGHT_TO_LEFT:
357
+ result = np.concatenate((padding, result), axis=1)
358
+ else:
359
+ result = np.concatenate((result, padding), axis=axis)
360
+ return result
361
+
362
+
363
+ app = typer.Typer()
364
+
365
+
366
+ def _generate_qr_with_markers(qr_str, text, title, direction):
367
+ if title:
368
+ #try to use standard size 10. Go bigger if 10 does not fit the data
369
+ try:
370
+ qr = segno.make_qr(qr_str, error="L", version=10)
371
+ except DataOverflowError:
372
+ qr = segno.make_qr(qr_str, error="L")
373
+ else:
374
+ qr = segno.make_qr(qr_str, error="L")
375
+
376
+ if(qr.mode != "alphanumeric"):
377
+ print("[bold yellow]Large QR:[/bold yellow] Provided URL is not alphanumeric!")
378
+ block_count = len(qr.matrix)
379
+ print(f"[bold]Size:[/bold] {block_count}")
380
+ print(f"[bold]Version:[/bold] {qr.version}")
381
+ print(f"[bold]Error Level:[/bold] {qr.error}")
382
+
383
+
384
+ qr_matrix = np.array(qr.matrix)
385
+ visual_marker = VisualMarker.generate_from_text(text.upper(), direction, block_count)
386
+ if title:
387
+ title_marker = VisualMarker.generate_from_text(title.upper(), direction, block_count)
388
+
389
+ append_axis = 1 if direction == Direction.TOP_TO_BOTTOM else 0
390
+ padding_dim = (4, block_count) if append_axis==0 else (block_count, qr.default_border_size)
391
+ padding= np.zeros(padding_dim, dtype=np.uint8)
392
+ if title:
393
+ combined_matrix = np.concatenate((title_marker, padding, qr_matrix, padding, visual_marker), axis=append_axis)
394
+ else:
395
+ combined_matrix = np.concatenate((qr_matrix, padding, visual_marker), axis=append_axis)
396
+
397
+ return combined_matrix
398
+
399
+ def save_qr_with_markers(url, text="PAC", title=None, direction = Direction.LEFT_TO_RIGHT, fmt='png', path='qr'):
400
+ combined_matrix = _generate_qr_with_markers(url, text="PAC", title=None, direction = Direction.LEFT_TO_RIGHT)
401
+ outfile = f'{path}.{fmt}'
402
+ match fmt:
403
+ case 'png':
404
+ writers.write_png(combined_matrix, combined_matrix.shape[::-1], out=outfile, border=9)
405
+ case 'svg':
406
+ writers.write_svg(combined_matrix, combined_matrix.shape[::-1], out=outfile, border=9)
407
+
408
+
409
+
410
+
411
+ def main(url: Annotated[str, typer.Argument(help="The PAC-ID to be rendered as QR.")],
412
+ outfile: Annotated[typer.FileBinaryWrite, typer.Option(help="The file the qr will be written to.")] = "qr.svg",
413
+ text: Annotated[str, typer.Option(help="The text of the PAC decoration.")] = "PAC",
414
+ direction: Annotated[Direction, typer.Option(help="The position/direction of the PAC decoration.")] = Direction.TOP_TO_BOTTOM):
415
+
416
+ save_qr_with_markers(url, text=text, direction=direction, path=outfile)
417
+
418
+
419
+
420
+ if __name__ == "__main__":
421
+ typer.run(main)
422
+