t2g.py 2 KB
import tempfile, os, os.path, inspect
import jinja2

class T2G (object) :
    def __init__ (self, env, header, body, trailer) :
        self.env = env
        self.tpl = jinja2.Template(body)
        self.trailer = trailer
        self.frames = []
        self.tmp = tempfile.TemporaryDirectory(suffix=".t2g", dir=os.getcwd())
        self.out = open(os.path.join(self.tmp.name, "frames.tex"), "w")
        self.out.write(header)
    def frame (self, duration) :
        env = self.env.copy()
        self.out.write("\n%%%%%% FRAME %s\n%s\n"
                       % (len(self.frames), self.tpl.render(**env)))
        self.frames.append(duration * 100)
    def gif (self, path, dpi=500) :
        self.out.write("\n%%%%%% TRAILER\n\n%s\n" % self.trailer)
        self.out.flush()
        cwd = os.getcwd()
        path = os.path.join(cwd, path)
        try :
            os.chdir(self.tmp.name)
            os.system("latexmk -pdf -cd -f frames.tex")
            print("extracting frames")
            os.system("convert -background white -alpha remove -density %s"
                      " frames.pdf frames.png" % dpi)
            print("building GIF")
            os.system("convert -layers OptimizeFrame -loop 0 %s %s"
                      % (" ".join("-delay %s frames-%s.png" % (duration, frame)
                                  for frame, duration in enumerate(self.frames)),
                         path))
            print("optimizing GIF")
            os.system("gifsicle --batch --optimize=3 %s" % path)
        finally :
            os.chdir(cwd)

__t2g__ = None

def load (path) :
    global __t2g__
    txt = open(path).read()
    header, txt = txt.split("%%FRAME")
    body, trailer = txt.split("%%/FRAME")
    stack = inspect.stack()
    __t2g__ = T2G(stack[1].frame.f_globals, header, body, trailer)

def frame (duration) :
    global __t2g__
    assert __t2g__, "no template loaded"
    __t2g__.frame(duration)

def gif (path, dpi=500) :
    global __t2g__
    assert __t2g__, "no template loaded"
    __t2g__.gif(path, dpi)