dotcode-decoder 1.0.0__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.
- dotcode/__init__.py +9 -0
- dotcode/code.py +915 -0
- dotcode/codes.py +247 -0
- dotcode/decoder.py +953 -0
- dotcode/decoder_90.py +390 -0
- dotcode/image.py +3 -0
- dotcode/images.py +200 -0
- dotcode/rs.py +212 -0
- dotcode/setup.py +26 -0
- dotcode/utils.py +36 -0
- dotcode_decoder-1.0.0.dist-info/METADATA +248 -0
- dotcode_decoder-1.0.0.dist-info/RECORD +14 -0
- dotcode_decoder-1.0.0.dist-info/WHEEL +5 -0
- dotcode_decoder-1.0.0.dist-info/top_level.txt +1 -0
dotcode/codes.py
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DotCode encoding/decoding logic
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .rs import DOT_TO_VAL, rs_correct, solve_ND_NC, mask_step, P
|
|
6
|
+
|
|
7
|
+
def decode_codesets(vals):
|
|
8
|
+
msg = []
|
|
9
|
+
cs = "C"
|
|
10
|
+
i = 0
|
|
11
|
+
shift_to = None
|
|
12
|
+
shift_n = 0
|
|
13
|
+
upper_A = False
|
|
14
|
+
upper_B = False
|
|
15
|
+
|
|
16
|
+
while i < len(vals):
|
|
17
|
+
v = vals[i]
|
|
18
|
+
|
|
19
|
+
if v == 107:
|
|
20
|
+
break
|
|
21
|
+
|
|
22
|
+
if v == 108:
|
|
23
|
+
msg.append("<FNC2>")
|
|
24
|
+
i += 1
|
|
25
|
+
continue
|
|
26
|
+
|
|
27
|
+
if v == 109:
|
|
28
|
+
msg.append("<FNC3>")
|
|
29
|
+
i += 1
|
|
30
|
+
continue
|
|
31
|
+
|
|
32
|
+
if v == 110:
|
|
33
|
+
upper_A = True
|
|
34
|
+
i += 1
|
|
35
|
+
continue
|
|
36
|
+
|
|
37
|
+
if v == 111:
|
|
38
|
+
upper_B = True
|
|
39
|
+
i += 1
|
|
40
|
+
continue
|
|
41
|
+
|
|
42
|
+
if v == 112:
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
if v > 112:
|
|
46
|
+
break
|
|
47
|
+
|
|
48
|
+
eff = shift_to if shift_n > 0 and shift_to else cs
|
|
49
|
+
|
|
50
|
+
if eff == "C" and v == 100:
|
|
51
|
+
break
|
|
52
|
+
|
|
53
|
+
if eff in ("A", "B") and v >= 107 and msg:
|
|
54
|
+
break
|
|
55
|
+
|
|
56
|
+
def tick():
|
|
57
|
+
nonlocal shift_n, shift_to
|
|
58
|
+
if shift_n > 0:
|
|
59
|
+
shift_n -= 1
|
|
60
|
+
if shift_n == 0:
|
|
61
|
+
shift_to = None
|
|
62
|
+
|
|
63
|
+
def uc(ch):
|
|
64
|
+
nonlocal upper_A, upper_B
|
|
65
|
+
if upper_A or upper_B:
|
|
66
|
+
ch = chr((ord(ch) + 128) % 256)
|
|
67
|
+
upper_A = False
|
|
68
|
+
upper_B = False
|
|
69
|
+
return ch
|
|
70
|
+
|
|
71
|
+
if eff == "C":
|
|
72
|
+
if v == 101:
|
|
73
|
+
cs = "A"
|
|
74
|
+
shift_to = None
|
|
75
|
+
shift_n = 0
|
|
76
|
+
elif v == 102:
|
|
77
|
+
shift_to = "B"
|
|
78
|
+
shift_n = 1
|
|
79
|
+
elif v == 103:
|
|
80
|
+
shift_to = "B"
|
|
81
|
+
shift_n = 2
|
|
82
|
+
elif v == 104:
|
|
83
|
+
shift_to = "B"
|
|
84
|
+
shift_n = 3
|
|
85
|
+
elif v == 105:
|
|
86
|
+
shift_to = "B"
|
|
87
|
+
shift_n = 4
|
|
88
|
+
elif v == 106:
|
|
89
|
+
cs = "B"
|
|
90
|
+
shift_to = None
|
|
91
|
+
shift_n = 0
|
|
92
|
+
elif 0 <= v <= 99:
|
|
93
|
+
msg.append(f"{v:02d}")
|
|
94
|
+
tick()
|
|
95
|
+
|
|
96
|
+
elif eff == "A":
|
|
97
|
+
if v == 101:
|
|
98
|
+
shift_to = "B"
|
|
99
|
+
shift_n = 1
|
|
100
|
+
elif v == 102:
|
|
101
|
+
cs = "B"
|
|
102
|
+
shift_to = None
|
|
103
|
+
shift_n = 0
|
|
104
|
+
elif v == 103:
|
|
105
|
+
shift_to = "C"
|
|
106
|
+
shift_n = 2
|
|
107
|
+
elif v == 104:
|
|
108
|
+
shift_to = "C"
|
|
109
|
+
shift_n = 3
|
|
110
|
+
elif v == 105:
|
|
111
|
+
shift_to = "C"
|
|
112
|
+
shift_n = 4
|
|
113
|
+
elif v == 106:
|
|
114
|
+
cs = "C"
|
|
115
|
+
shift_to = None
|
|
116
|
+
shift_n = 0
|
|
117
|
+
elif 0 <= v <= 63:
|
|
118
|
+
msg.append(uc(chr(32 + v)))
|
|
119
|
+
tick()
|
|
120
|
+
elif 64 <= v <= 95:
|
|
121
|
+
msg.append(uc(chr(v - 64)))
|
|
122
|
+
tick()
|
|
123
|
+
else:
|
|
124
|
+
tick()
|
|
125
|
+
|
|
126
|
+
elif eff == "B":
|
|
127
|
+
if v == 101:
|
|
128
|
+
shift_to = "A"
|
|
129
|
+
shift_n = 1
|
|
130
|
+
elif v == 102:
|
|
131
|
+
cs = "A"
|
|
132
|
+
shift_to = None
|
|
133
|
+
shift_n = 0
|
|
134
|
+
elif v == 103:
|
|
135
|
+
shift_to = "C"
|
|
136
|
+
shift_n = 2
|
|
137
|
+
elif v == 104:
|
|
138
|
+
shift_to = "C"
|
|
139
|
+
shift_n = 3
|
|
140
|
+
elif v == 105:
|
|
141
|
+
shift_to = "C"
|
|
142
|
+
shift_n = 4
|
|
143
|
+
elif v == 106:
|
|
144
|
+
cs = "C"
|
|
145
|
+
shift_to = None
|
|
146
|
+
shift_n = 0
|
|
147
|
+
elif 0 <= v <= 95:
|
|
148
|
+
msg.append(uc(chr(32 + v)))
|
|
149
|
+
tick()
|
|
150
|
+
elif v == 96:
|
|
151
|
+
msg.append("\r\n")
|
|
152
|
+
tick()
|
|
153
|
+
elif v == 97:
|
|
154
|
+
msg.append("\t")
|
|
155
|
+
tick()
|
|
156
|
+
elif v == 98:
|
|
157
|
+
msg.append(chr(28))
|
|
158
|
+
tick()
|
|
159
|
+
elif v == 99:
|
|
160
|
+
msg.append(chr(29))
|
|
161
|
+
tick()
|
|
162
|
+
elif v == 100:
|
|
163
|
+
msg.append(chr(30))
|
|
164
|
+
tick()
|
|
165
|
+
else:
|
|
166
|
+
tick()
|
|
167
|
+
|
|
168
|
+
i += 1
|
|
169
|
+
|
|
170
|
+
result = "".join(msg)
|
|
171
|
+
|
|
172
|
+
if len(result) >= 12:
|
|
173
|
+
for pad in ["27", "28", "29", "00", "99", "14"]:
|
|
174
|
+
if result.endswith(pad):
|
|
175
|
+
result = result[:-2]
|
|
176
|
+
break
|
|
177
|
+
|
|
178
|
+
return result if result else None
|
|
179
|
+
|
|
180
|
+
def decode_bits(bitstr):
|
|
181
|
+
s = "".join(bitstr.split())
|
|
182
|
+
|
|
183
|
+
if not all(c in "01" for c in s) or len(s) < 20:
|
|
184
|
+
return None
|
|
185
|
+
|
|
186
|
+
ones = s.count("1") / len(s) * 100
|
|
187
|
+
if ones > 78 or ones < 8:
|
|
188
|
+
return None
|
|
189
|
+
|
|
190
|
+
mask_id = (int(s[0]) << 1) | int(s[1])
|
|
191
|
+
cw_bits = [s[2+k*9:2+(k+1)*9] for k in range((len(s)-2)//9)]
|
|
192
|
+
vals = []
|
|
193
|
+
era = []
|
|
194
|
+
|
|
195
|
+
for idx, b in enumerate(cw_bits):
|
|
196
|
+
v = None
|
|
197
|
+
if len(b) == 9 and b.count("1") == 5:
|
|
198
|
+
v = DOT_TO_VAL.get(b)
|
|
199
|
+
|
|
200
|
+
if v is None:
|
|
201
|
+
vals.append(None)
|
|
202
|
+
era.append(idx)
|
|
203
|
+
else:
|
|
204
|
+
vals.append(v)
|
|
205
|
+
|
|
206
|
+
NW = len(vals)
|
|
207
|
+
ND, NC = solve_ND_NC(NW)
|
|
208
|
+
|
|
209
|
+
if ND is None:
|
|
210
|
+
return None
|
|
211
|
+
|
|
212
|
+
if all(v is not None for v in vals[:ND]):
|
|
213
|
+
step = mask_step(mask_id)
|
|
214
|
+
dv = [(vals[i] - step * i) % P for i in range(ND)]
|
|
215
|
+
msg = decode_codesets(dv)
|
|
216
|
+
|
|
217
|
+
if msg and len(msg) >= 4:
|
|
218
|
+
if not any(c in msg for c in ['/', '\\', '\x00', '\xff', ')', '(', '<']):
|
|
219
|
+
return msg
|
|
220
|
+
|
|
221
|
+
res = rs_correct(mask_id, vals, ND, NC, era)
|
|
222
|
+
|
|
223
|
+
if res is None:
|
|
224
|
+
return None
|
|
225
|
+
|
|
226
|
+
corrected, _ = res
|
|
227
|
+
step = mask_step(mask_id)
|
|
228
|
+
dv = [(corrected[i] - step * i) % P for i in range(ND)]
|
|
229
|
+
msg = decode_codesets(dv)
|
|
230
|
+
|
|
231
|
+
if msg and len(msg) >= 4:
|
|
232
|
+
if not any(c in msg for c in ['/', '\\', '\x00', '\xff', ')', '(', '<']):
|
|
233
|
+
return msg
|
|
234
|
+
|
|
235
|
+
return None
|
|
236
|
+
|
|
237
|
+
def is_valid_message(msg):
|
|
238
|
+
if msg is None or len(msg) < 4:
|
|
239
|
+
return False
|
|
240
|
+
|
|
241
|
+
if msg.isdigit() and len(msg) > 20:
|
|
242
|
+
return False
|
|
243
|
+
|
|
244
|
+
if any(c in msg for c in ['/', '\\', '\x00', '\xff', ')', '(', '<']):
|
|
245
|
+
return False
|
|
246
|
+
|
|
247
|
+
return True
|