Initial commit

This commit is contained in:
xenia 2019-01-15 14:31:28 -05:00
commit 3f631fe1ab
3 changed files with 211 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
.venv
*.pyc
*.pyo
*.pyd
__pycache__
*.egg-info
.env
.ycm_extra_conf.py

183
asciicast2latex/__init__.py Normal file
View File

@ -0,0 +1,183 @@
#!/usr/bin/env python3
import ast
import json
from pathlib import Path
import pyte
import re
import shutil
import subprocess
import sys
import tempfile
def dconf_ls(path):
output = subprocess.check_output(["dconf", "list", str(path) + "/"])
return [path / line for line in output.decode().strip().split("\n")]
def dconf_read(path):
output = subprocess.check_output(["dconf", "read", str(path)])
return ast.literal_eval(output.decode())
def rgb_to_arr(rgb):
return "".join(["{0:02x}".format(int(x)) for x in re.findall("[0-9]+", rgb)])
def get_terminal_conf():
profiles = dconf_ls(Path("/org/gnome/terminal/legacy/profiles:/"))
bgc = dconf_read(profiles[0] / "background-color")
fgc = dconf_read(profiles[0] / "foreground-color")
palette = dconf_read(profiles[0] / "palette")
return (rgb_to_arr(bgc),
rgb_to_arr(fgc),
[rgb_to_arr(c) for c in palette])
def get_screen(filename):
width = 0
height = 0
data = ""
with open(filename, "r") as file:
hdr = True
for strline in file:
line = json.loads(strline)
if hdr:
width = line['width']
height = line['height']
hdr = False
else:
data += line[2]
# snip out weird escape code (??? what is this, some sort of window title?)
data = re.sub(r'\x1b_.*?\x1b\\', "", data)
screen = pyte.screens.HistoryScreen(width, height, history=9999999)
stream = pyte.Stream(screen)
stream.feed(data)
lines = [l for l in screen.history.top]
for i in range(len(screen.buffer)):
lines.append(screen.buffer[i])
return (width, height, lines)
def convert(filename, output):
print("Generating latex")
(bgc, fgc, palette) = get_terminal_conf()
print(bgc, fgc, palette)
(w, h, scr)= get_screen(filename)
latexsrc = toTikz(scr, w, len(scr), bgc, fgc, palette)
print("Compiling")
with tempfile.TemporaryDirectory() as tmpdir:
tmpdir = Path(tmpdir)
with (tmpdir / "console.tex").open("w") as file:
file.write(latexsrc)
subprocess.check_call(["xelatex", "console.tex"], cwd=str(tmpdir))
shutil.copyfile(tmpdir / "console.pdf", output)
# adapted from https://github.com/misc0110/asciicast2vector (MIT License)
def sanitizeLatexChar(c):
if c in "&%$#_{}":
return "\\" + c
elif c == "~":
return "\\textasciitilde"
elif c == "^":
return "\\textasciicircum"
elif c == "\\":
return "\\textbackslash"
else:
return c
def toTikz(lines, max_col, max_row, bgc, fgc, palette):
pline = ""
pline += tikzHeader(lines, bgc, fgc, palette)
pline += "\\begin{tikzpicture}[yscale=-1]\\ttfamily\n"
lspace = 1.8
pline += "\\draw[fill=%s,draw=none] (-1em,-1em) rectangle +(%.4fem,%dem);\n" % ("defaultbg", (max_col + 4) / lspace, max_row + 2)
for y in range(len(lines)):
line = lines[y]
for x in range(len(line)):
char = line[x]
if char.data == ' ' and ((char.reverse == False and char.bg == "default") or (char.reverse and char.fg == "default")):
continue
if char.reverse:
fg = char.bg
bg = char.fg
else:
fg = char.fg
bg = char.bg
if fg == "default":
fg = "defaultfg"
if bg == "default":
bg = "defaultbg"
content = sanitizeLatexChar(char.data)
if char.bold:
content = "\\textbf{" + content + "}"
if char.italics:
content = "\\textit{" + content + "}"
pline += "\\draw[fill=%s,draw=none] (%.4fem,%dem) rectangle +(0.6em,1em) node[pos=.5, anchor=base, yshift=-0.5ex] {\\textcolor{%s}{%s}};\n" % (bg, x / lspace, y, fg, content)
pline += "\\end{tikzpicture}"
pline += tikzFooter()
return pline
def color2tex(color, name=None):
if name is None:
name = color
line = ""
line += "\\definecolor{" + name + "}{RGB}{"
line += str(int(color[:2], 16)) + ","
line += str(int(color[2:4], 16)) + ","
line += str(int(color[4:], 16)) + "}\n"
return line
def tikzColors(lines, bgc, fgc, palette):
line = ""
line += color2tex(bgc, "defaultbg")
line += color2tex(fgc, "defaultfg")
for i,c in enumerate(["black", "red", "green", "brown", "blue", "magenta", "cyan", "white"]):
line += color2tex(palette[i], c)
colors = set(["default", "black", "red", "green", "brown", "blue", "magenta", "cyan", "white"])
for y in range(len(lines)):
_line = lines[y]
for x in range(len(_line)):
char = _line[x]
if not char.bg in colors:
colors.add(char.bg)
line += color2tex(char.bg)
if not char.fg in colors:
colors.add(char.fg)
line += color2tex(char.fg)
return line
def tikzHeader(lines, bgc, fgc, palette):
line = "\\documentclass[class=minimal,border=0pt]{standalone}\n"
line += "\\usepackage[utf8]{inputenc}\n"
line += "\\usepackage{tikz}\n"
line += "\\usepackage[utf8]{inputenc}\n"
line += "\\usepackage{fontspec}\n"
line += "\\setmonofont[Ligatures=TeX]{Hack Regular}\n"
# line += "\\usetikzlibrary{external}\n"
# line += "\\tikzexternalize\n"
line += "\\usepackage{xcolor}\n"
line += "\\usepackage{amssymb}\n"
line += "\\usepackage{pmboxdraw}\n"
line += tikzColors(lines, bgc, fgc, palette)
line += "\\begin{document}\n"
return line
def tikzFooter():
return "\\end{document}\n"
def main():
if len(sys.argv) < 3:
print("Usage: asciicast2latex <cast file> <pdf output>")
sys.exit(-1)
convert(sys.argv[1], sys.argv[2])
if __name__ == "__main__":
main()

20
setup.py Normal file
View File

@ -0,0 +1,20 @@
from setuptools import setup
setup(name='asciicast2latex',
version='0.1',
description='Converts asciinema casts to latex (and compiles to pdf)',
url='https://notabug.org/haskal/asciicast2latex',
author='haskal',
author_email='haskal@bepis.xyz',
license='GPLv3',
packages=['asciicast2latex'],
install_requires=[
"pyte"
],
include_package_data=True,
entry_points={
'console_scripts': [
'asciicast2latex=asciicast2latex:main'
]
},
zip_safe=False)