Spaces:
Running
Running
Download api.py from Vishwasaidev/quill: direct link, hf CLI and curl.
- Browser
- Download file 2.03 kB
-
https://huggingface.co/spaces/Vishwasaidev/quill/resolve/main/api.py
- Command line
-
hf download hf://spaces/Vishwasaidev/quill/api.py
-
curl -L -o api.py https://huggingface.co/spaces/Vishwasaidev/quill/resolve/main/api.py
2.03 kB
| """The one web entry point, shared by server.py (Heroku, local) and the in-browser Hugging Face Space (Pyodide).""" | |
| import base64 | |
| import io | |
| import threading | |
| from urllib.parse import parse_qs | |
| from PIL import features | |
| from quill import INKS, STYLES, Glyphs, animate, is_cjk, render | |
| G = None | |
| LOCK = threading.Lock() | |
| def render_query(query): | |
| """URL query string -> (HTTP status, dict with an image data URL in `src`, or an `error`).""" | |
| global G | |
| q = {k: v[0] for k, v in parse_qs(query).items()} | |
| text = q.get("text", "").replace("\\n", "\n").strip() | |
| style, ink, anim = q.get("style", "italic"), q.get("ink") or None, bool(q.get("animate")) | |
| if style not in STYLES or (ink and ink not in INKS) or not text: | |
| return 400, dict(error="need text, a known style and ink") | |
| limit = (24 if anim else 36) if is_cjk(text) else (120 if anim else 300) # keeps a small dyno in budget | |
| if len(text) > limit: | |
| return 400, dict(error=f"please keep it under {limit} characters") | |
| try: | |
| size = min(max(int(q.get("size", 72)), 32), 140) | |
| except ValueError: | |
| size = 72 | |
| if is_cjk(text) != (style == "shodo"): # same switch quill.compose makes | |
| style = "shodo" if is_cjk(text) else "brush" | |
| with LOCK: # one render at a time bounds memory | |
| G = G or Glyphs() | |
| args = (text, style, ink, size, 1800, bool(q.get("flourish")), bool(q.get("initial")), q.get("seal", "")[:4], G) | |
| if anim: | |
| fmt = "WEBP" if features.check("webp") else "GIF" # some Pillow builds lack WebP | |
| data = animate(*args, fmt=fmt) | |
| return 200, dict(style=style, src=f"data:image/{fmt.lower()};base64," + base64.b64encode(data).decode()) | |
| buf = io.BytesIO() | |
| render(*args).save(buf, "PNG") | |
| return 200, dict(style=style, src="data:image/png;base64," + base64.b64encode(buf.getvalue()).decode()) | |