pyltk/pyltk/documentclass.py

190 lines
5.6 KiB
Python

# -*- encoding: utf-8 -*-
from collections import Iterable
from .element import element
import enum
import os
import subprocess
class CompileResult(enum.Enum):
ALREADY_EXISTS = 1
SUCCESS = 2
ERROR = 3
class documentclass(element):
classname = "standalone"
templates = {
"package": "\\usepackage{pkgoptions}{{{pkgname}}}",
"options": "[{content}]",
"def": "\\def\\{name}{nargs}{{{value}}}",
"content": """\\documentclass{options}{{{classname}}}
{packages}
{preamble}
\\begin{{document}}
{content}
\\end{{document}}
""",
}
output_aux_folder = ".pyltk"
auto_tex_folder_name = "tex2pdf"
def __init__(self, classname, childrens=[], options=[], packages=[], preamble=[]):
super().__init__(parent=None, childrens=childrens)
self.options = options
self.classname = classname
self.packages = {}
self.add_packages(packages)
self.preamble = []
self.add2preamble(preamble)
def add_packages(self, packages):
if type(packages) is str:
self.packages[packages] = True
elif type(packages) is dict:
self.packages.update(packages)
else:
self.packages.update({p: True for p in packages})
def add2preamble(self, value):
if isinstance(value, Iterable):
self.preamble.extend(value)
else:
self.preamble.append(value)
def add_def(self, name, nargs, value):
self.preamble.append(
self.fmt().format(
"def",
{
"name": name,
"nargs": "#{}".format(nargs) if nargs > 0 else "",
"value": value,
},
)
)
def format_preamble(self, defs):
return "\n".join(str(p) for p in self.preamble)
def format_packages(self, pkgs):
out = []
for pkg in sorted(pkgs):
options = ""
if pkgs[pkg] is not True:
options = self.format_options(pkgs[pkg])
out.append(
self.fmt().format("package", {"pkgname": pkg, "pkgoptions": options})
)
return "\n".join(out)
def content(self):
return self.fmt().format(
"content",
{
"options": self.format_options(self.options),
"packages": self.format_packages(self.packages),
"preamble": self.format_preamble(self.preamble),
"classname": self.classname,
"content": super().content(),
},
)
def compile(self, outfile, infile="auto", outlog=None, compile_twice=False):
""" Compile the given document into a PDF file.
If infile is not None, and infile already exists, the document
will only be compiled if the content of infile is different
from this document or if infile is newer than the output
document.
Parameters:
- outfile Name of the output file (pdf extension may
be omitted).
- infile Name of the file to which latex code must be
written. If None, a temporary file will be created
and then erased. If 'auto', file will be saved in
an automatic folder for future use.
- outlog Output loging stream (default stdout).
- compile_twice Set to true to compile the document twice.
"""
outdir = os.path.dirname(outfile)
outfile = os.path.basename(outfile)
res = CompileResult.ERROR
output_aux_folder = os.path.join(outdir, self.output_aux_folder)
auto_tex_folder = os.path.join(output_aux_folder, self.auto_tex_folder_name)
# Make aux directory
os.makedirs(output_aux_folder, exist_ok=True)
# Remove extension if exists
if outfile.endswith(".pdf"):
outfile = outfile[:-4]
to_delete = False
if infile == "auto":
os.makedirs(auto_tex_folder, exist_ok=True)
infile = "{}/{}.tex".format(auto_tex_folder, outfile)
if infile is None:
to_delete = True
infile = os.path.join(outdir, ".pyltk-{}.tex".format(outfile))
to_create = True
new_content = self.content()
# If the input file already exists...
if os.path.exists(infile):
with open(infile, "r") as infd:
old_content = infd.read()
# Content has not changed
if (
old_content == new_content
and os.path.exists(os.path.join(outdir, outfile) + ".pdf")
and os.path.getctime(infile)
<= os.path.getctime(os.path.join(outdir, outfile) + ".pdf")
):
to_create = False
if to_create:
with open(infile, "w") as infd:
infd.write(self.content())
call = [
"pdflatex",
"-halt-on-error",
"-output-directory",
output_aux_folder,
"-jobname",
outfile,
infile,
]
res = subprocess.call(call, stdout=outlog)
if compile_twice:
res = subprocess.call(call, stdout=outlog)
if res == 0:
os.replace(
os.path.join(output_aux_folder, outfile + ".pdf"),
os.path.join(outdir, outfile + ".pdf"),
)
res = CompileResult.SUCCESS
else:
res = CompileResult.ERROR
else:
res = CompileResult.ALREADY_EXISTS
if to_delete:
os.remove(infile)
return res