1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
#!/usr/bin/env python3
# Author: cirakg & claude ai
# Summary: Generate source/codes/QrTables.mc (QR error-correction block structure) from segno's published constants.
"""QR block structure for versions 1-40 and levels L, M, Q, H (ISO/IEC 18004
table 9), taken from segno so it isn't typed by hand. Each entry is
[ec codewords per block, blocks in group 1, data codewords per group-1
block, blocks in group 2, data codewords per group-2 block].
gen_qr_tables.py [--out source/codes/QrTables.mc]
"""
from __future__ import annotations
import argparse
from pathlib import Path
from typing import Any, cast
import segno.consts as c
LEVELS = [c.ERROR_LEVEL_L, c.ERROR_LEVEL_M, c.ERROR_LEVEL_Q, c.ERROR_LEVEL_H]
# segno.consts.ECC: version -> level -> tuple of EC(num_blocks, num_total, num_data); untyped in segno.
ECC = cast(dict[int, dict[int, tuple[Any, ...]]], c.ECC)
def table() -> list[list[int]]:
rows = []
for version in range(1, 41):
for level in LEVELS:
groups = ECC[version][level]
ec = groups[0].num_total - groups[0].num_data
g1 = groups[0]
g2 = groups[1] if len(groups) > 1 else None
assert all(g.num_total - g.num_data == ec for g in groups)
rows.append([ec, g1.num_blocks, g1.num_data, g2.num_blocks if g2 else 0, g2.num_data if g2 else 0])
return rows
def alignment() -> list[list[int]]:
"""Alignment pattern centre coordinates per version (ISO/IEC 18004 annex E)."""
# segno starts at version 2; version 1 has no alignment pattern.
return [[]] + [list(c.ALIGNMENT_POS[v - 2]) for v in range(2, 41)]
def render(rows: list[list[int]]) -> str:
lines = []
for version in range(1, 41):
cells = ", ".join(str(n) for level in range(4) for n in rows[(version - 1) * 4 + level])
lines.append(f" {cells}{'' if version == 40 else ','} // version {version}: L, M, Q, H")
return (
"// Author: cirakg & claude ai\n"
"// Summary: QR error-correction block structure per version and level (generated from segno; do not edit).\n"
"//\n"
"// Derived from Segno (BSD-3-Clause). See THIRD_PARTY_NOTICES.md and LICENSES/segno.txt.\n"
"// Generated by tools/gen_qr_tables.py. For version v (1-40) and level e (0 L, 1 M, 2 Q, 3 H), the five\n"
"// numbers at index ((v - 1) * 4 + e) * 5 are: EC codewords per block, group-1 blocks, data codewords\n"
"// per group-1 block, group-2 blocks, data codewords per group-2 block (ISO/IEC 18004 table 9).\n\n"
"import Toybox.Lang;\n\nmodule QrTables {\n const BLOCKS = [\n"
+ "\n".join(lines)
+ "\n ];\n\n // Alignment pattern centres (row and column coordinates) per version, from version 1.\n"
+ " const ALIGNMENT = [\n"
+ ",\n".join(f" {pos}" for pos in alignment())
+ "\n ];\n}\n"
)
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument("--out", type=Path, default=Path("source/codes/QrTables.mc"))
args = parser.parse_args(argv)
args.out.write_text(render(table()), encoding="utf-8")
print(f"wrote the QR block table to {args.out}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
|