summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/gen_code_vectors.py45
-rw-r--r--tools/tests/test_barcode_tools.py8
-rw-r--r--tools/ui_flow.py9
3 files changed, 58 insertions, 4 deletions
diff --git a/tools/gen_code_vectors.py b/tools/gen_code_vectors.py
index 5ce2611..1472bb7 100644
--- a/tools/gen_code_vectors.py
+++ b/tools/gen_code_vectors.py
@@ -23,6 +23,7 @@ from typing import Any, cast
import barcode_ref as ref
import ppf.datamatrix.codec_ascii # noqa: F401 (registers the datamatrix.ascii codec)
import segno
+from aztec_code_generator import AztecCode
from pdf417gen import encode as pdf417_encode
from ppf.datamatrix import DataMatrix
@@ -287,6 +288,42 @@ def pdf417_reference(payload: str, columns: int, level: int) -> tuple[int, int,
return len(rows), len(rows[0]), packed.hex()
+AZTEC_EC = [10, 23, 36, 50]
+
+
+def aztec_cases(rng: random.Random, count: int) -> list[tuple[str, int]]:
+ """(payload, EC option 0-3) with one character class each (capitals, small
+ letters or digits, plus spaces): for these the reference's optimal mode
+ sequence and the watch's encodation give the same bits, so the symbols
+ must match exactly. Sizes reach full range with reference grids."""
+ fixed = [
+ ("A", 1),
+ ("HELLO WORLD", 1),
+ ("hello world", 0),
+ ("0042 7731", 2),
+ ("FAKE TICKET " * 10, 1),
+ ("abc def " * 40, 3),
+ ("1234567890" * 60, 1),
+ ("Z" * 900, 0),
+ ]
+ out = list(fixed)
+ for _ in range(count):
+ alphabet = rng.choice([string.ascii_uppercase + " ", string.ascii_lowercase + " ", string.digits + " "])
+ n = rng.randint(1, 200)
+ payload = "".join(rng.choice(alphabet) for _ in range(n)).strip() or "X"
+ out.append((payload, rng.randrange(4)))
+ return out
+
+
+def aztec_reference(payload: str, option: int) -> tuple[int, str]:
+ """aztec_code_generator's (size, matrix as hex of row-major bits)."""
+ code = AztecCode(payload, ec_percent=AZTEC_EC[option])
+ bits = "".join(str(b) for row in code.matrix for b in row)
+ bits += "0" * (-len(bits) % 8)
+ packed = bytes(int(bits[i : i + 8], 2) for i in range(0, len(bits), 8))
+ return code.size, packed.hex()
+
+
def mc_string(text: str) -> str:
"""A Monkey C string literal body for text (quotes, backslashes, controls escaped)."""
out = []
@@ -342,6 +379,11 @@ def render(rows: list[tuple[int, str, int, str]], seed: int) -> str:
n_rows, width, hexbits = reference
pdf_lines.append(f' ["{mc_string(payload)}", {columns}, {level}, {n_rows}, {width}, "{hexbits}"]')
pdf_body = ",\n".join(pdf_lines)
+ aztec_lines = []
+ for payload, option in aztec_cases(random.Random(seed + 4), 16):
+ size, hexbits = aztec_reference(payload, option)
+ aztec_lines.append(f' ["{payload}", {option}, {size}, "{hexbits}"]')
+ aztec_body = ",\n".join(aztec_lines)
return (
"// Author: cirakg & claude ai\n"
"// Summary: zxing-cpp reference module rows for the linear barcode encoders (generated; do not edit).\n"
@@ -358,6 +400,9 @@ def render(rows: list[tuple[int, str, int, str]], seed: int) -> str:
+ "\n ];\n\n // [payload, columns, level, pdf417gen rows, width, one module row per PDF417 row as hex]\n"
+ " const PDF417 = [\n"
+ pdf_body
+ + "\n ];\n\n // [payload, EC option, aztec_code_generator size, its matrix as hex of row-major bits]\n"
+ + " const AZTEC = [\n"
+ + aztec_body
+ "\n ];\n}\n"
)
diff --git a/tools/tests/test_barcode_tools.py b/tools/tests/test_barcode_tools.py
index 5a2e999..f63b3fe 100644
--- a/tools/tests/test_barcode_tools.py
+++ b/tools/tests/test_barcode_tools.py
@@ -109,3 +109,11 @@ def test_pdf417_reference_rows() -> None:
reference = gen_code_vectors.pdf417_reference("Hello, World!", 3, 2)
assert reference is not None and reference[0] == 7 and reference[1] == 17 * (3 + 4) + 1
assert gen_code_vectors.pdf417_reference("ab", 30, 0) is None # one row: below the minimum of 3
+
+
+def test_aztec_reference_is_compact_for_short_text() -> None:
+ size, hexbits = gen_code_vectors.aztec_reference("HELLO WORLD", 1)
+ assert size == 15 # compact, one layer
+ assert len(bytes.fromhex(hexbits)) == (15 * 15 + 7) // 8
+ image = ref.render_matrix(bytes.fromhex(hexbits), (15, 15), 3, quiet=2)
+ assert ref.decode(image, ref.AZTEC) == ["HELLO WORLD"]
diff --git a/tools/ui_flow.py b/tools/ui_flow.py
index eea3e17..e54b530 100644
--- a/tools/ui_flow.py
+++ b/tools/ui_flow.py
@@ -65,6 +65,7 @@ MEMBER_QR = ( # source/app/Fixtures.mc: a version 6 QR
)
PARCEL_DM = "PARCEL-FAKE-0042-7731" # source/app/Fixtures.mc: Data Matrix
TICKET_PDF417 = "Boarding pass FAKE 0042 seat 12A" # source/app/Fixtures.mc: PDF417
+RAIL_AZTEC = "RAIL-FAKE-0042 Zone 1-3" # source/app/Fixtures.mc: Aztec
QR_TIMEOUT_S = 30.0 # the QR is encoded in timer steps the first time
@@ -432,8 +433,8 @@ def flow_lockout(f: Flow) -> None:
def flow_code(f: Flow) -> None:
- """The EAN-13 gym card (index 6), the QR (7), Data Matrix (8) and PDF417 (9)
- cards: their screenshots must decode."""
+ """The EAN-13 gym card (index 6), the QR (7), Data Matrix (8), PDF417 (9)
+ and Aztec (10) cards: their screenshots must decode."""
f.act()
f.back() # leave the waiting PIN screen
f.expect("list")
@@ -455,8 +456,8 @@ def flow_code(f: Flow) -> None:
if encoded is not None:
print(f"[{f.sim.device_dir.name}] QR {encoded.group(1)} encoded in {encoded.group(2)} ms")
f.shot("code_qr", expect_code=MEMBER_QR)
- # Data Matrix (8) and PDF417 (9), also encoded as jobs the first time.
- for name, expected in (("code_datamatrix", PARCEL_DM), ("code_pdf417", TICKET_PDF417)):
+ # Data Matrix (8), PDF417 (9) and Aztec (10), also encoded as jobs.
+ for name, expected in (("code_datamatrix", PARCEL_DM), ("code_pdf417", TICKET_PDF417), ("code_aztec", RAIL_AZTEC)):
f.act()
f.back()
f.expect("list")