ansi2png.py (2549B)
1 #!/usr/bin/env python3 2 """Convierte una captura de tmux (capture-pane -e -p) a PNG, para revisar la TUI. 3 Uso: tmux capture-pane -e -p -t SES | python3 tools/ansi2png.py salida.png [cols rows]""" 4 import re, sys 5 from PIL import Image, ImageDraw, ImageFont 6 7 def xterm(n): 8 base = [(0,0,0),(205,0,0),(0,205,0),(205,205,0),(0,0,238),(205,0,205),(0,205,205),(229,229,229), 9 (127,127,127),(255,0,0),(0,255,0),(255,255,0),(92,92,255),(255,0,255),(0,255,255),(255,255,255)] 10 if n < 16: return base[n] 11 if n < 232: 12 n -= 16; lv = [0,95,135,175,215,255] 13 return (lv[n//36], lv[(n//6)%6], lv[n%6]) 14 v = 8 + (n-232)*10; return (v,v,v) 15 16 out = sys.argv[1] 17 cols = int(sys.argv[2]) if len(sys.argv) > 2 else 160 18 rows = int(sys.argv[3]) if len(sys.argv) > 3 else 50 19 text = sys.stdin.read().split("\n")[:rows] 20 font = ImageFont.truetype("/usr/share/fonts/TTF/DejaVuSansMono.ttf", 14) 21 bold = ImageFont.truetype("/usr/share/fonts/TTF/DejaVuSansMono-Bold.ttf", 14) 22 cw, ch = 8, 17 23 DFG, DBG = (220,220,220), (18,18,24) 24 img = Image.new("RGB", (cols*cw, rows*ch), DBG) 25 d = ImageDraw.Draw(img) 26 for y, line in enumerate(text): 27 fg, bg, b, rev = DFG, DBG, False, False 28 x = 0 29 for tok in re.split(r"(\x1b\[[0-9;]*m)", line): 30 if tok.startswith("\x1b["): 31 ps = [int(p) if p else 0 for p in tok[2:-1].split(";")] 32 i = 0 33 while i < len(ps): 34 p = ps[i] 35 if p == 0: fg, bg, b, rev = DFG, DBG, False, False 36 elif p == 1: b = True 37 elif p == 7: rev = True 38 elif p == 22: b = False 39 elif p == 27: rev = False 40 elif p == 39: fg = DFG 41 elif p == 49: bg = DBG 42 elif p == 38 and i+2 < len(ps) and ps[i+1] == 5: fg = xterm(ps[i+2]); i += 2 43 elif p == 48 and i+2 < len(ps) and ps[i+1] == 5: bg = xterm(ps[i+2]); i += 2 44 elif 30 <= p <= 37: fg = xterm(p-30) 45 elif 40 <= p <= 47: bg = xterm(p-40) 46 elif 90 <= p <= 97: fg = xterm(p-90+8) 47 elif 100 <= p <= 107: bg = xterm(p-100+8) 48 i += 1 49 continue 50 for c in tok: 51 f, g = (bg, fg) if rev else (fg, bg) 52 d.rectangle([x*cw, y*ch, x*cw+cw-1, y*ch+ch-1], fill=g) 53 if c == "▀": 54 d.rectangle([x*cw, y*ch, x*cw+cw-1, y*ch+ch//2-1], fill=f) 55 elif c != " ": 56 d.text((x*cw, y*ch), c, font=bold if b else font, fill=f) 57 x += 1 58 img.save(out)