"""make_maker.py: turn a dollz layout (schema vonk-dollz/1) into ONE self-contained HTML doll maker. CC0. python make_maker.py layout.json folder_with_pngs out.html [--title "My doll maker"] [--zoom 4] Every PNG is embedded as a data URI, so the page makes no outside requests: host it anywhere. Visitors click pieces to dress the doll, press Random, and save a PNG. Positions come straight from the layout (x, y = top-left corner in base pixels; draw order = order of "slots"), so nobody guesses coordinates. Needs maker_template.html next to this script (download both from /dollz/ on Vonk's site). """ import base64 import json import sys from pathlib import Path HERE = Path(__file__).resolve().parent MARK = "/*__DOLL__*/null" def data_uri(p): return "data:image/png;base64," + base64.b64encode(Path(p).read_bytes()).decode() def make(layout, folder, title="Doll maker", zoom=4, template=None): folder = Path(folder) tpl = template if template is not None else (HERE / "maker_template.html").read_text(encoding="utf-8") assert MARK in tpl, "maker_template.html has no " + MARK b = layout["base"] doll = { "schema": "vonk-dollz/1", "title": title, "zoom": int(zoom), "base": {"src": data_uri(folder / b["src"]), "w": b["w"], "h": b["h"]}, "slots": [{"slot": s["slot"], "label": s.get("label", s["slot"])} for s in layout["slots"]], "items": [{"id": i["id"], "slot": i["slot"], "label": i.get("label", i["id"]), "src": data_uri(folder / i["src"]), "x": i["x"], "y": i["y"], "w": i["w"], "h": i["h"]} for i in layout["items"]], "worn": list(layout.get("worn", [])), } slots = {s["slot"] for s in doll["slots"]} for i in doll["items"]: # items in a slot the layout forgot: put that slot on top, as the placer does if i["slot"] not in slots: doll["slots"].append({"slot": i["slot"], "label": i["slot"]}) slots.add(i["slot"]) js = json.dumps(doll, separators=(",", ":")).replace("<", "\\u003c") # safe inside