190 lines
5.6 KiB
Python
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
|