summaryrefslogtreecommitdiff
path: root/tools/gen_pdf417_tables.py
blob: 7a66677c6c46ba20ee1e0485274973bd2afc120e (plain) (blame)
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
77
#!/usr/bin/env python3
# Author:  cirakg & claude ai
# Summary: Generate the PDF417 table resources (patterns, EC factors, text table) from pdf417gen's published tables.
"""PDF417 tables for the watch (ISO/IEC 15438), taken from pdf417gen so the
2,787 bar patterns aren't typed by hand. Five JSON resources (the compiler
warns about resources over 8 KB):

- codes0, codes1, codes2: the 17-module pattern of every codeword (0-928)
  in clusters 0, 3 and 6
- ec: the Reed-Solomon factors (mod 929) for levels 0-8
- text: per text-compaction submode (upper, lower, mixed, punctuation), the
  value of ASCII 0-126 in that submode, or -1

The app loads them only while it encodes a PDF417 code.

    gen_pdf417_tables.py [--out-dir resources/pdf417]
"""

from __future__ import annotations

import argparse
import json
from pathlib import Path

from pdf417gen import codes, data

SUBMODES = [data.UPPER, data.LOWER, data.MIXED, data.PUNCT]


HEADER = {"_author": "cirakg & claude ai"}


def tables() -> dict[str, object]:
    """Resource name -> JSON content."""
    text = [[data.CHARACTERS_LOOKUP.get(c, {}).get(mode, -1) for c in range(127)] for mode in SUBMODES]
    out: dict[str, object] = {}
    for k in range(3):
        out[f"codes{k}"] = {
            **HEADER,
            "_summary": f"PDF417 cluster {3 * k} patterns (generated; do not edit).",
            "v": list(codes.CODES[k]),
        }
    out["ec"] = {
        **HEADER,
        "_summary": "PDF417 EC factors per level (generated; do not edit).",
        "v": [list(f) for f in data.ERROR_CORRECTION_FACTORS],
    }
    out["text"] = {**HEADER, "_summary": "PDF417 text submode values (generated; do not edit).", "v": text}
    return out


def resources_xml(names: list[str]) -> str:
    rows = "\n".join(f'    <jsonData id="Pdf417{name.capitalize()}" filename="{name}.json" />' for name in names)
    return (
        "<!--\n  Author:  cirakg & claude ai\n"
        "  Summary: The PDF417 tables as JSON resources, loaded only while a PDF417 code is encoded (generated).\n-->\n"
        f"<resources>\n{rows}\n</resources>\n"
    )


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
    parser.add_argument("--out-dir", type=Path, default=Path("resources/pdf417"))
    args = parser.parse_args(argv)
    args.out_dir.mkdir(parents=True, exist_ok=True)
    for old in args.out_dir.glob("*.json"):
        old.unlink()
    content = tables()
    for name, value in content.items():
        (args.out_dir / f"{name}.json").write_text(json.dumps(value, separators=(",", ":")) + "\n", encoding="utf-8")
    (args.out_dir / "pdf417.xml").write_text(resources_xml(list(content)), encoding="utf-8")
    print(f"wrote {len(content)} PDF417 table resources to {args.out_dir}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())