#!/usr/bin/env python3 """Simple tex jinja2 Precompiler. With slightly different syntax: {{ ... }} => \VAR{ ... } for Expressions to print to the template output {% ... %} => \BLOCK{ ... } for Statements {# ... #} => \#{ ... } for Comments not included in the template output # ... ## => %$ ... %# for Line Statements Usage: pretex [] [options] Arguments: Compiles every given file ending with ".j2.tex" into respective ".tex" file. [default: "**/*.j2.tex"] Options: -h --help Show this screen. -p --pdf Also compiles rendered ".tex" file to pdf. -o --out= Save output to file. -d --data= YAML files with data for the template. -t --tex= LaTeX compiler to use [default: pdflatex] -a --arg= LaTeX compiler arguments [default: -synctex=1 -interaction=nonstopmode -file-line-error] -b --bib= bibTeX compiler to use. [default: bibtex] -c --copy= Gather copies of all resulting pdf files in a folder. --chain= Define the render command. [default: {TEX} {ARG} {document}.tex, {BIB} {document}, {TEX} {ARG} {document}.tex, {TEX} {ARG} {document}.tex] --clean Clean aux files. -f --files= Files to clean. [default: *.aux, *.bbl, *.blg, *.idx, *.ind, *.lof, *.lot, *.out, *.toc, *.acn, *.acr, *.alg, *.glg, *.glo, *.gls, *.ist, *.fls, *.log, *.fdb_latexmk] """ import yaml import glob import jinja2 import os from docopt import docopt import dateparser import datetime import subprocess import functools import shlex from shutil import copyfile class RelEnvironment(jinja2.Environment): """Override join_path() to enable relative template paths.""" def join_path(self, template, parent): return os.path.normpath(os.path.join(os.path.dirname(parent), template)) def datetime_filter(dt, format=None): """Jinja template filter to format a datetime object.""" if dt is None: # By default, render an empty string. return '' if not isinstance(dt, datetime.datetime): dt = dateparser.parse(dt, settings={'DATE_ORDER': 'DMY'}) if format is None: # No format is given in the template call. # Use a default format. formatted_date = dt.strftime('%Y-%m-%d - %A') formatted_time =\ dt.strftime('%I:%M%p').lstrip('0').lower() formatted = '%s at %s' %\ (formatted_date, formatted_time) else: formatted = dt.strftime(format) return formatted def get_latex_toolchain(arguments, document): return arguments["--chain"].format( TEX=arguments["--tex"], ARG=arguments["--arg"], BIB=arguments["--bib"], document=document).split(",") def compile_all(data, templates, pdf, cmd, copy, clean): if templates is None: templates = "**/*.j2.tex" for tex_file in glob.glob(templates, recursive=True): if tex_file[-7:] == ".j2.tex": compiled_file = f"{tex_file[:-7]}.tex" print(compile_file(tex_file, data, compiled_file)) if not pdf: continue path, filename = os.path.split(compiled_file) for command in cmd(filename[:-4]): cwd = path if cwd == '': cwd = None subprocess.call(shlex.split(command), cwd=cwd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) if copy: if not os.path.exists(copy): os.makedirs(copy) copyfile(f"{tex_file[:-7]}.pdf", os.path.join(copy, f"{filename[:-4]}.pdf")) if clean: for ending in clean.split(","): for file in glob.glob(os.path.join(path, ending.strip()), recursive=True): os.remove(file) def compile_file(tex_file, data, file=None): template = latex_jinja_env.get_template(os.path.abspath(tex_file)) path, filename = os.path.split(tex_file) rendered = template.render(**data, load_yaml=functools.partial(load_yaml, path=path)) if file is None: return rendered f = open(file, 'w') f.write(rendered) f.close() return f'"{tex_file}" successful rendered into "{file}"' def load_yaml(filename, path=""): with open(os.path.join(path,filename)) as file: data = yaml.load(file, Loader=yaml.FullLoader) return data if __name__ == '__main__': arguments = docopt(__doc__) latex_jinja_env = RelEnvironment( block_start_string = '\\BLOCK{', block_end_string = '}', variable_start_string = '\\VAR{', variable_end_string = '}', comment_start_string = '\\#{', comment_end_string = '}', line_statement_prefix = '%ยง', line_comment_prefix = '%#', trim_blocks = True, autoescape = False, loader = jinja2.FileSystemLoader(os.path.abspath('/')), ) latex_jinja_env.filters['datetime'] = datetime_filter if arguments['--data']: data = load_yaml(arguments["--data"]) else: data = {} cmd = functools.partial(get_latex_toolchain, arguments) compile_all(data, arguments[''], arguments['--pdf'], cmd, arguments['--copy'], arguments['--clean'] and arguments['--files'])