# -*- coding: utf-8 -*-
"""
pyTex - set up a LaTeX environment
==================================
The pyTex class is designed to set up a LaTeX environment.
For document class (:class:`~latex.pyDocumentClass.pyDocumentClass`),
packages (:class:`~latex.pyPackages.pyPackages`),
title page (:class:`~latex.pyTitlePage.pyTitlePage`),
and BibTeX (:class:`~latex.pyBibtex.pyBibtex`) self-contained classes are defined.
required classes and methods::
from latex.pyDocumentClass import pyDocumentClass
from latex.pyPackages import pyPackages
from latex.pyTitlePage import pyTitlePage
from latex.pyBibtex import pyBibtex
"""
import os
import shutil
import subprocess
from .pyBibtex import PyBibtex
from .pyDocumentClass import PyDocumentClass
from .pyPackages import PyPackages
from .pyTexData import VALIDATION_OPTIONAL_SETTINGS, PAVAYO_OPTIONAL_SETTINGS
from .pyTitlePage import PyTitlePage
# section types given by number
_chapterTypes = {-1: "part", 0: "chapter", 1: "section", 2: "subsection",
3: "subsubsection", 4: "paragraph", 5: "subparagraph"}
# label text given by number
_numberingTypes = {-1: "undefined", 0: "sec", 1: "fig", 2: "eq", 3: "tab"}
# internal method to determine the label id
def _labeling(label, iType, labelid=-1, nType=-1, noNumbering=False):
"""return the label id
The function returns either an user-defined label or an automatic label id.
If a label is not wanted an empty string is returned instead.
:param label: user defined label text (either a string or
:param iType: label type used if no label is given (It must be a key of the dictionary _numberingTypes)
:param labelid: label index if a list of labels is given in 'label'
:param nType: consecutive numbering used if no label is given
:param noNumbering: flag to indicate if labeling is wanted
:type label: string
:type iType: integer
:type labelid: integer
:type nType: integer
:type noNumbering: boolean
:return: label string
:rtype: string
"""
if noNumbering:
labeling = ""
else:
if label:
if isinstance(label, str):
labelingText = label
elif 0 <= labelid < len(label):
labelingText = label[labelid]
else:
raise TypeError("_labeling: wrong iType of label")
else:
try:
labelingText = "%s:%d" % (_numberingTypes[iType], nType)
except KeyError:
print(f"_labeling: wrong iType = {iType}")
raise
labeling = f"\\label{{{labelingText}}}"
return labeling
# main class to create a latex file and compile it
[docs]class PyTex:
"""
main class to handle a LaTeX object
:param fileName: base name of latex project (usually without '.tex' extension)
:type fileName: string
:param defaultSettings: default settings for particular projects like 'pavayo': [equation', 'pavayo', 'validation', 'presentation']
:type defaultSettings: string
:param documentClass: latex document class
:type documentClass: string
:param documentclassOptions: latex document class options
:type documentclassOptions: list of string
:param packageSettings: list of packages (see pyPackages)
:type packageSettings: string
:param toc: flag for table of contents
:type toc: boolean
:param lof: flag for list of figures
:type lof: boolean
:param lot: flag for list of tables
:type lot: boolean
:param content: content of document
:type content: list of strings
:param verbose: output level
:type verbose: integer
:param latexPath: path to latex executable
:type latexPath: string
:param dvipngPath: path to dvipng executable
:type dvipngPath: string
:param pdflatexPath: path to pdflatex executable
:type pdflatexPath: string
:param bibtexPath: path to bibtex executable
:type bibtexPath: string
:param verbose: verbosity level
:type verbose: integer
"""
CHARS_TO_REPLACE = {"%": r"\%", "$": r"\$", "{": r"\{", "}": r"\}", "_": r"\_", "#": r"\#", "&": r"\&", "ä": '\\"a', "ö": '\\"o', "ü": '\\"u'}
[docs] @staticmethod
def escapeChars(text, encode=None):
"""Escapes characters, which would otherwise not printed correctly in TeX.
:param text: text in which the characters will be replaced
:param encode: optional encoding for text
:type text: str
:type encode: str or None
:return: text with replaced characters
:rtype: str
"""
if encode is not None:
text = text.encode(encode)
for char, replacement in PyTex.CHARS_TO_REPLACE.items():
try:
text = text.replace(char, replacement)
except UnicodeDecodeError:
text = text.encode('utf-8')
text = text.replace(char, replacement)
return text
def __init__(self, fileName="", defaultSettings="",
documentClass="scrreprt", documentclassOptions=None,
packageSettings=None, toc=True, lof=False, lot=False, content=None,
latexPath='latex', dvipngPath='dvipng', pdflatexPath='pdflatex', bibtexPath='bibtex',
verbose=0):
"""initialize class
:param fileName: base name of latex project (usually without '.tex' extension)
:type fileName: string
:param defaultSettings: default settings for particular projects like 'pavayo': [equation', 'pavayo', 'validation', 'presentation']
:type defaultSettings: string
:param documentClass: latex document class
:type documentClass: string
:param documentclassOptions: latex document class options
:type documentclassOptions: list of string
:param packageSettings: list of packages (see pyPackages)
:type packageSettings: list of string tuples
:param toc: flag for table of contents
:type toc: boolean
:param lof: flag for list of figures
:type lof: boolean
:param lot: flag for list of tables
:type lot: boolean
:param content: content of document
:type content: list of strings
:param verbose: output level
:type verbose: integer
:param latexPath: path to latex executable
:type latexPath: string
:param dvipngPath: path to dvipng executable
:type dvipngPath: string
:param pdflatexPath: path to pdflatex executable
:type pdflatexPath: string
:param bibtexPath: path to bibtex executable
:type bibtexPath: string
:param verbose: verbosity level
:type verbose: integer
parameters of class:
preamble - document class settings (see pyDocumentClass)
packages - input variable 'packageSettings'
optionalSettings - list of optional settings placed after package list and before \begin{document}
titlepage - titlepage class (see pyTitlePage)
tableOfContents - input variable 'toc'
listOfFigures - input variable 'lof'
listOfTables - input variable 'lot'
content - input variable 'content'
bibtex - bibtex class if needed (see pyBibtex)
fileName - input variable 'fileName'
verbose - input variable 'verbose'
numberOfFigures - consecutive numbering of figures
numberOfTables - consecutive numbering of tables
numberOfEquations - consecutive numbering of equations
numberOfSections - consecutive numbering of sections
latexPath - path to latex executable
dvipngPath - path to dvipng executable
pdflatexPath - path to pdflatex executable
bibtexPath - path to bibtex executable
"""
documentclassOptions = documentclassOptions or ["a4paper", "10pt"]
self.verbose = verbose
self.optionalSettings = []
self.tableOfContents = toc
self.listOfFigures = lof
self.listOfTables = lot
self.titlepage = None
self.bibtex = None
self.numberOfFigures = 0
self.numberOfTables = 0
self.numberOfEquations = 0
self.numberOfSections = 0
self.defaultSettings = defaultSettings
if defaultSettings:
defaultSettings = defaultSettings.lower()
if defaultSettings == "pavayo":
self._setPAVAYO()
elif defaultSettings == "equation":
self._setEquation()
elif defaultSettings == "validation":
self._setValidation()
elif defaultSettings == "presentation":
self._setPresentation()
else:
raise ValueError("pyTex: wrong entry for 'defaultSettings'")
else:
self.preamble = PyDocumentClass(documentClass=documentClass, options=documentclassOptions, verbose=verbose)
self.packages = PyPackages(default=defaultSettings, verbose=verbose)
# add further packages
if packageSettings:
for name, options in packageSettings:
self.packages.addPackage(name, options)
if not content:
content = []
self.content = content
self.appendix = []
self.fileName = os.path.splitext(fileName)[0]
self.latexPath = latexPath
self.dvipngPath = dvipngPath
self.pdflatexPath = pdflatexPath
self.bibtexPath = bibtexPath
if self.verbose:
print("pyTex initialised.")
def __getContent(self, appendix):
"""return pointer either to the main content or to the appendix
:param appendix: flag to add this entry to the appendix instead of the main content
:type appendix: boolean
:return: pointer either to the main content or to the appendix
:rtype: list of strings
"""
if appendix:
return self.appendix
else:
return self.content
# default settings for pavayo
def _setPAVAYO(self):
"""default settings for pavayo
"""
self.preamble = PyDocumentClass(documentClass="scrreprt", options=["a4paper", "10pt"])
self.optionalSettings.extend(PAVAYO_OPTIONAL_SETTINGS.split("\n"))
if self.verbose > 1:
print("default settings for PAVAYO are used")
# default settings for equation
def _setEquation(self):
"""default settings for equation
"""
self.preamble = PyDocumentClass(documentClass="article")
self.optionalSettings.append("\\pagestyle{empty}")
self.tableOfContents = False
if self.verbose > 1:
print("default settings for equation are used")
def _setValidation(self):
"""default settings for validation
"""
self.preamble = PyDocumentClass(documentClass="scrreprt", options=["a4paper", "10pt"])
self.optionalSettings.extend(VALIDATION_OPTIONAL_SETTINGS.split("\n"))
if self.verbose > 1:
print("default settings for VALIDATION are used")
def _setPresentation(self):
"""default settings for DLR presentation
"""
self.preamble = PyDocumentClass(documentClass="dlrbeamer", options=["9pt", "english", "mathserif", "ATBack", "AT"])
if self.verbose > 1:
print("default settings for DLR presentation are used")
[docs] def setTitle(self, titleType="auto", content="", title="", author="", date=""):
"""create title page
:param titleType: type of title page, options: 'user', 'auto'
:param content: content of title page (used if titleType = 'user')
:param title: title of document (used if titleType = 'auto')
:param author: author of document (used if titleType = 'auto')
:param date: date of document (used if titleType = 'auto')
:type titleType: string
:type content: string
:type title: string
:type author: string
:type date: string
"""
self.titlepage = PyTitlePage(titleType=titleType, content=content, title=title, author=author, date=date, verbose=self.verbose)
[docs] def setBibtex(self, sources, style="plain"):
"""set bibtex settings using class pyBibtex
:param sources: list of used bibtex files
:param style: bibtex style
:type sources: string
:type style: string
"""
self.bibtex = PyBibtex(sources, style=style)
if self.verbose > 1:
print("bibtex is settings are as follows:")
print(" bibtex sources:", self.bibtex.sources)
print(" bibtex style :", self.bibtex.style)
[docs] def setfileName(self, fileName=""):
"""set base name of latex project
:param fileName: base name of latex project (optional, default: "")
:type fileName: string
"""
if fileName:
self.fileName = fileName
if self.verbose > 1:
print(f"base name of project is set to {self.fileName}")
else:
raise ValueError("setfileName: specify fileName", 0)
[docs] def addSectionTitle(self, title, chapterType=0, shortTitle="", label="", noNumbering=False, appendix=False):
"""add section title to content
:param title: title
:param chapterType: header level (optional, default: "")
:param shortTitle: short tile (optional, default: "")
:param label: corresponding label (optional, default: "")
:param noNumbering: flag to indicate if labeling is wanted (optional, default: False)
:param appendix: flag to add this entry to the appendix instead of the main content (optional, default: False)
:type title: string
:type chapterType: int
:type shortTitle: string
:type label: string
:type noNumbering: boolean
:type appendix: boolean
"""
# document classes that include chapter
includeChapter = ["report", "book", "scrreprt", "scrbook"]
if self.preamble.documentClass not in includeChapter:
chapterType += 1
if chapterType < -1 or chapterType > 5:
raise ValueError(f"addSectionTitle: wrong chapterType = {chapterType}")
if noNumbering:
option = "*"
elif shortTitle:
option = f"[{shortTitle}]"
else:
option = ""
self.__getContent(appendix).append("\\%s%s{%s}" % (_chapterTypes[chapterType], option, title))
if not noNumbering:
labeling = _labeling(label, 0, nType=self.numberOfSections)
self.__getContent(appendix).append(labeling)
self.numberOfSections += 1
# add tabular environment
def __addGenericTabularEnvironment(self, array, environmentName, nCols=0, setCols="", appendix=False, headerSeparator=None, rowSeparator=None):
"""add tabular environment to content
:param array: contents of table as a 2D-array(list)
:param environmentName: name of environment (tabular / longtable)
:param nCols: header number of columns (optional, default: 0)
:param setCols: settings for columns (optional, default: "")
:param appendix: flag to add this entry to the appendix instead of the main content (optional, default: False)
:param headerSeparator: separate first row of table from rest, e.g. by "\\hline" (optional, default: "")
:param rowSeparator: separate each row of table, e.g. by "\\hline" (optional, default: "")
:type array: list of strings
:type environmentName: string
:type nCols: string
:type setCols: string
:type appendix: boolean
:type headerSeparator: string
:type rowSeparator: string
"""
textTable = []
textTable.append("\n\\begin{%s}{%s}" % (environmentName, setCols))
for iRow, tabRow in enumerate(array):
# @todo: extend implementation for use of multi-column
if len(tabRow) != nCols:
raise ValueError("addTabular: entries in row %d differ" % (iRow + 1))
tabRowString = " & ".join([str(tabEntry) for tabEntry in tabRow])
textTable.append(f"{tabRowString}\\\\")
if iRow == 0 and headerSeparator:
textTable.append(headerSeparator)
if not iRow == 0 and not iRow == (len(array) - 1) and rowSeparator:
textTable.append(rowSeparator)
textTable.append(f"\\end{{{environmentName}}}\n")
self.__getContent(appendix).append("\n".join(textTable))
# add tabular environment
[docs] def addTabular(self, array, **kwargs):
"""add tabular environment to content
:param array: contents of table as a 2D-array(list)
:param nCols: header number of columns (optional, default: 0)
:param setCols: settings for columns (optional, default: "")
:param appendix: flag to add this entry to the appendix instead of the main content (optional, default: False)
:param headerSeparator: separate first row of table from rest, e.g. by "\\hline" (optional, default: "")
:type array: list of strings
:type nCols: string
:type setCols: string
:type appendix: boolean
:type headerSeparator: string
"""
self.__addGenericTabularEnvironment(array, "tabular", **kwargs)
[docs] def addLongtable(self, array, **kwargs):
"""add longtable environment to content
:param array: contents of table as a 2D-array(list)
:param nCols: header number of columns (optional, default: 0)
:param setCols: settings for columns (optional, default: "")
:param appendix: flag to add this entry to the appendix instead of the main content (optional, default: False)
:param headerSeparator: separate first row of table from rest, e.g. by "\\hline" (optional, default: "")
:type array: list of strings
:type nCols: string
:type setCols: string
:type appendix: boolean
:type headerSeparator: string
"""
if kwargs["headerSeparator"]:
kwargs["headerSeparator"] += "\\endhead"
self.__addGenericTabularEnvironment(array, "longtable", **kwargs)
# add table environment
[docs] def addTable(self, array, position="ht", nCols=0, setCols="", caption="", label="", noNumbering=False, appendix=False):
"""add table environment to content
:param array: contents of table as a 2D-array(list)
:param position: position of table in document (optional, default: "ht")
:param nCols: header number of columns (optional, default: 0)
:param setCols: settings for columns (optional, default: "")
:param caption: caption of table (optional, default: "")
:param label: corresponding label (optional, default: "")
:param noNumbering: flag to indicate if labeling is wanted (optional, default: False)
:param appendix: flag to add this entry to the appendix instead of the main content (optional, default: False)
:type array: list of strings
:type position: string
:type nCols: int
:type setCols: string
:type caption: string
:type label: string
:type noNumbering: boolean
:type appendix: boolean
"""
if len(setCols) != nCols or nCols <= 0:
raise ValueError("addTable: nCols = %d and setCols = '%s' differ" % (nCols, setCols))
if noNumbering:
option = "*"
else:
option = ""
self.__getContent(appendix).append("\n\\begin{table%s}[%s]" % (option, position))
self.addTabular(nCols=nCols, setCols=setCols, array=array, appendix=appendix)
if caption:
self.__getContent(appendix).append(f"\\caption{{{caption}}}")
if not noNumbering:
labeling = _labeling(label, 3, nType=self.numberOfTables)
self.__getContent(appendix).append(labeling)
self.__getContent(appendix).append(f"\\end{{table{option}}}\n")
self.numberOfTables += 1
# add figure environment
# add subigure environment
[docs] def addMiniPage(self, content, position="ht", width="\\textwidth", appendix=False):
"""add mini page to content
:param content: contents of mini page
:param position: position of table in document (optional, default: "ht")
:param width: width of mini page
:param appendix: flag to add this entry to the appendix instead of the main content (optional, default: False)
:type content: list of strings
:type width: string
:type position: string
:type appendix: boolean
"""
miniPage = list()
miniPage.append(fr'\begin{{minipage}}[{position}]{{{width}}}')
miniPage.append(content)
miniPage.append(r'\end{minipage}')
self.__getContent(appendix).append("\n".join(miniPage))
# add text
[docs] def addText(self, text, appendix=False):
"""add text to content
**parameters**:
* **text** - text string
**optional parameters**:
* **appendix** - flag to add this entry to the appendix instead of the main content
"""
self.__getContent(appendix).append(text)
[docs] def addItemize(self, items, appendix=False):
"""Add a itemize environment to the content.
:param items: list of strings, containing the items
:type items: iterable
:param appendix: flag to add this entry to the appendix instead of the main content
:type appendix: boolean
"""
self.__getContent(appendix).append("\\begin{itemize}")
for item in items:
self.__getContent(appendix).append(f" \\item {item!s}")
self.__getContent(appendix).append("\\end{itemize}\n")
# add optional settings
[docs] def addOptionalSettings(self, text):
"""add optional settings to latex document
**parameters**:
* **text** - text string
"""
self.optionalSettings.append(text)
# add verbatim environment
[docs] def addVerbatim(self, text, options=None, appendix=False):
"""add verbatim environment to content
**parameters**:
* **text** - text string
**optional parameters**:
* **appendix** - flag to add this entry to the appendix instead of the main content
"""
if not "fancyvrb" in self.packages.packagelist.getPackages():
self.packages.addPackage("fancyvrb")
if options:
self.__getContent(appendix).append(f"\\begin{{Verbatim}}[{options}]")
else:
self.__getContent(appendix).append("\\begin{Verbatim}")
self.__getContent(appendix).append(text)
self.__getContent(appendix).append("\\end{Verbatim}")
# add equation(s)
[docs] def addEquations(self, text, number=1, label=None, noNumbering=False, appendix=False):
"""add equation(s) to content
**parameters**:
* **text** - formula or list of formulas
**optional parameters**:
* **number** - number of equations
* **label** - corresponding label
* **noNumbering** - flag to indicate if labeling is wanted
* **appendix** - flag to add this entry to the appendix instead of the main content
"""
mathEnv = "equation"
mathTable = []
if number > 1:
if not isinstance(text, list) or number != len(text):
raise ValueError("addEquations: entries in 'text' and 'number' differ")
else:
mathEnv = "eqnarray"
elif number <= 0:
raise ValueError(f"addEquations: number must be greater or equal 1, number = {number}")
if noNumbering:
option = "*"
else:
option = ""
if label:
if number > 1 and number != len(label):
raise ValueError("addEquations: label has wrong dimension")
else:
if not isinstance(label, str):
raise TypeError("addEquations: label must be a string for number = 1")
mathTable.append("\\begin{%s%s}" % (mathEnv, option))
if number > 1:
for idF, formula in enumerate(text):
labeling = _labeling(label, 2, labelid=idF, nType=self.numberOfEquations, noNumbering=noNumbering)
mathTable.append("%s\\\\%s" % (formula, labeling))
self.numberOfEquations += 1
else:
labeling = _labeling(label, 2, nType=self.numberOfEquations, noNumbering=noNumbering)
mathTable.append("%s%s" % (text, labeling))
self.numberOfEquations += 1
mathTable.append("\\end{%s%s}\n" % (mathEnv, option))
self.__getContent(appendix).append("\n".join(mathTable))
# add file
[docs] def addFile(self, filename, newPage=False, exists=True, appendix=False):
"""add file 'filename' to content
**parameters**:
* **filename** - name of file
**optional parameters**:
* **newPage** - flag whether 'include' or 'input' is used
* **exists** - flag if file exists
* **appendix** - flag to add this entry to the appendix instead of the main content
"""
if newPage:
inputtype = "include"
else:
inputtype = "input"
if exists:
self.__getContent(appendix).append("\\%s{%s}" % (inputtype, filename))
else:
figTable = []
figTable.append('{\\color{red}')
figTable.append('\\begin{verbatim}')
figTable.append(' ' + '!!' * 38)
figTable.append(' ' + '!!' * 38)
figTable.append(' !!!!' + ' ' * 34 + '!!!!')
figTable.append(' !!!! Missing file: %s !!!!' % filename.ljust(45))
figTable.append(' !!!!' + ' ' * 34 + '!!!!')
figTable.append(' ' + '!!' * 38)
figTable.append(' ' + '!!' * 38)
figTable.append('\\end{verbatim}}')
self.__getContent(appendix).append("\n".join(figTable))
# add content of file
[docs] def addFileContent(self, filename, sectionName="", chapterType=0, fontsize=10, verbatim=True, appendix=False):
"""add content of file 'filename' to content within a verbatim environment
**parameters**:
* **filename** - name of file
**optional parameters**:
* **sectionName** - title
* **chapterType** - header level
* **fontsize** - font size
* **verbatim** - flag if verbatim environment should be used
* **appendix** - flag to add this entry to the appendix instead of the main content
"""
try:
infohandle = open(filename, 'r')
except IOError:
print("WARNING: cannot open file '%s'" % filename)
figTable = []
figTable.append('{\\color{red}')
figTable.append('\\begin{verbatim}')
figTable.append(' ' + '!!' * 38)
figTable.append(' ' + '!!' * 38)
figTable.append(' !!!!' + ' ' * 34 + '!!!!')
figTable.append(' !!!! Missing file: %s !!!!' % filename.ljust(45))
figTable.append(' !!!!' + ' ' * 34 + '!!!!')
figTable.append(' ' + '!!' * 38)
figTable.append(' ' + '!!' * 38)
figTable.append('\\end{verbatim}}')
self.addText("\n".join(figTable), appendix=appendix)
else:
infolines = infohandle.readlines()
self.addText("\\clearpage", appendix=appendix)
if sectionName:
self.addSectionTitle(sectionName, chapterType=chapterType, appendix=appendix)
self.addText("\\fontsize{%f}{%f}\\selectfont" % (fontsize, fontsize * 0.8), appendix=appendix)
if verbatim:
self.addVerbatim("".join(infolines), appendix=appendix)
else:
self.addText("".join(infolines), appendix=appendix)
self.addText("\\normalsize", appendix=appendix)
infohandle.close()
[docs] def addWatermark(self, text="DRAFT", color="red", translucency=50):
"""Add text as watermark in specified color and translucency
**optional parameters**:
* **text** - text
* **color** - color (xcolor package is used)
* **translucency** - translucency in per cent
"""
if 0 < translucency < 100:
colortext = f"{color}!{translucency}"
else:
colortext = color
watermark = ["\\makeatletter", "\\newcommand\\BackgroundPicture[3]{",
"\\AtTextCenter{", "\\makebox(0,0)[c]{\\resizebox{\\textwidth}{!}{",
"\\rotatebox{60}{\\textsf{\\textbf{\\color{%s}%s}}}}}}}" % (colortext, text),
"\\makeatother", "\\AddToShipoutPicture{\\BackgroundPicture}"]
self.packages.addPackage("xcolor", options="usenames, dvipsnames")
self.packages.addPackage("eso-pic")
self.optionalSettings.append("\n".join(watermark))
# create latex file
[docs] def writeLatexFile(self):
"""create latex file
"""
if self.fileName:
try:
latexfilehandle = open(self.fileName, 'w')
except IOError:
raise RuntimeError(f"Cannot open '{self.fileName}' to write the TEX file!")
else:
latexfilehandle.write(str(self.preamble))
latexfilehandle.write(str(self.packages))
if self.optionalSettings:
for optSetting in self.optionalSettings:
latexfilehandle.write(f"{optSetting}\n")
latexfilehandle.write("\n\\begin{document}\n")
if self.titlepage:
latexfilehandle.write(str(self.titlepage))
if self.tableOfContents:
latexfilehandle.write("\\tableofcontents\n")
latexfilehandle.write("\\clearpage\n\n")
if self.listOfFigures:
latexfilehandle.write("\\listoffigures\n")
latexfilehandle.write("\\clearpage\n\n")
if self.listOfTables:
latexfilehandle.write("\\listoftables\n")
latexfilehandle.write("\\clearpage\n\n")
if isinstance(self.content, str):
latexfilehandle.write(f"{self.content}\n")
else:
latexfilehandle.write("\n".join(self.content))
if self.appendix:
latexfilehandle.write("\n\\begin{appendix}")
latexfilehandle.write("\n".join(self.appendix))
latexfilehandle.write("\\end{appendix}\n")
if self.bibtex:
latexfilehandle.write("\n%s\n" % str(self.bibtex))
latexfilehandle.write("\n\\end{document}\n")
latexfilehandle.close()
if self.verbose:
print(f"latex file {self.fileName} has been written.")
else:
raise RuntimeError("No file specified to create a TEX file; use 'setfileName' to specify a TEX file name before writing!")
# compile latex project as png
def _compileDVIPNG(self, msgfileName="latexmsgs", imageSize="tight", resolution=1200, background="transparent",
compressionLevel=9, outputPath=""):
"""compile latex project as png
**optional parameters**:
* **msgfileName** - name of message file
* **imageSize** - image size as string (also accepts 'bbox' and 'tight')
* **resolution** - output resolution
* **background** - background color (TeX-style color or 'transparent')
* **compressionLevel** - png compression level
* **outputPath** - place a copy of the output file at this path
**return parameters**:
* return code of dvi run
``
Usage: dvipng [OPTION]... FILENAME[.dvi]
-D # Output resolution
-l # Last page to be output
-o f Output file, '%d' is pagenumber
-O c Image offset
-p # First page to be output
-pp #,#.. Page list to be output
-q* Quiet operation
-T c Image size (also accepts '-T bbox' and '-T tight')
These do not correspond to dvips options:
-bd # Transparent border width in dots
-bd s Transparent border fallback color (TeX-style color)
-bg s Background color (TeX-style color or 'Transparent')
--dvinum* Use TeX page numbers in output filenames
-fg s Foreground color (TeX-style color)
--gif Output GIF images (dvigif default)
--height* Output the image height on stdout
--picky When a warning occurs, don't output image
--png Output PNG images (dvipng default)
-z # PNG compression level
``
"""
fileName = self.fileName
options = ["--shell-escape", "-interaction=nonstopmode"]
if self.verbose:
print(f"\n Generating pdf document: {fileName}")
latexDirFile = os.path.split(fileName)
if latexDirFile[0]:
latexDir = latexDirFile[0]
else:
latexDir = "."
latexmsgs = open("%s/%s" % (latexDir, msgfileName), 'a')
latexCmd = self.latexPath + " " + " ".join(options) + " " + latexDirFile[1]
retcode1 = subprocess.call(latexCmd.split(), cwd=latexDir, stdout=latexmsgs, stderr=latexmsgs)
if retcode1:
print("WARNING: Error %d running latex for %s.\n" % (retcode1, fileName))
return
dviOptions = []
if imageSize:
dviOptions.append(f"-T {imageSize}")
if resolution:
dviOptions.append(f"-D {resolution}")
if background:
dviOptions.append(f"-bg {background}")
if compressionLevel:
dviOptions.append(f"-z {compressionLevel}")
dviCmd = self.dvipngPath + " " + " ".join(dviOptions) + " " + fileName
retcode1 = subprocess.call(dviCmd.split(), cwd=latexDir, stdout=latexmsgs, stderr=latexmsgs)
if retcode1:
print("WARNING: Error while executing dvipng")
return
elif outputPath and os.path.abspath(outputPath) != os.getcwd():
pngFileName = fileName + ".png"
shutil.copy(pngFileName, outputPath)
if self.verbose:
print(" Done.")
tempext = ['.aux', '.dvi', '.log']
for te in tempext:
tempfile = fileName + te
if os.path.exists(tempfile):
os.remove(tempfile)
return retcode1
# compile latex project using pdflatex
def _compilePdfLatex(self, msgfileName="latexmsgs", outputPath=""):
"""compile latex project using pdflatex
**optional parameters**:
* **msgfileName** - name of message file
* **outputPath** - place a copy of the output file at this path
**return parameters**:
* return code of pdflatex runs or bibtex run
``options for pdflatex
Usage: pdftex [OPTION]... [TEXNAME[.tex]] [COMMANDS]
-draftmode switch on draft mode (generates no output PDF)
[-no]-file-line-error disable/enable file:line:error style messages
-halt-on-error stop processing at the first error
-jobname=STRING set the job name to STRING
-output-comment=STRING use STRING for DVI file comment instead of date
(no effect for PDF)
-output-directory=DIR use DIR as the directory to write files to
-output-format=FORMAT use FORMAT for job output; FORMAT is `dvi' or `pdf'
[-no]-shell-escape disable/enable \write18{SHELL COMMAND}
-interaction=STRING set interaction mode (STRING=batchmode/nonstopmode/scrollmode/errorstopmode)``
"""
fileName = self.fileName
if self.verbose:
print(f"\n Generating pdf document: {fileName}")
head, tail = os.path.split(self.fileName)
options = ["--shell-escape", "-interaction=nonstopmode", f"-output-directory={head}"]
pdfCmd = self.pdflatexPath + " " + " ".join(options) + " " + tail
retcode1 = 0
retcode2 = 0
with open(os.path.join(head, msgfileName), "a") as latexmsgs:
for iRun in range(3):
if not retcode1:
# Call pdflatex several times to get table of contents
retcode1 = subprocess.call(pdfCmd.split(), stdout=latexmsgs, stderr=latexmsgs)
else:
print(f"\n Error running pdflatex for {fileName}")
break
if self.bibtex and iRun == 0 and not retcode1:
fileNameWOExt = os.path.splitext(fileName)[0]
bibCmd = [self.bibtexPath, fileNameWOExt]
retcode2 = subprocess.call(bibCmd, stdout=latexmsgs, stderr=latexmsgs)
# retcode2 == 2: the biliography is empty, so no citations are in the document
if retcode2 and retcode2 != 2:
print(f"\n Problems occurred while running bibtex for {fileName}. Check generated bibliography.\n")
if not retcode1:
if outputPath and os.path.abspath(outputPath) != os.getcwd():
pdfFileName = fileName + ".pdf"
shutil.copy(pdfFileName, outputPath)
if self.verbose:
print(" Done.")
return retcode1 or (retcode2 if retcode2 != 2 else 0)
# compile latex project
[docs] def compileLatexFile(self, cplType="pdf", msgfileName="latexmsgs", outputPath=""):
"""compile latex project
**optional parameters**:
* **cplType** - compile type: ['pdf', 'png', 'ps', 'pspdf']
* **msgfileName** - name of message file
* **outputPath** - place a copy of the output file at this path
**return**:
* return code from subprocess call
"""
returnCode = -1
if self.fileName:
cplType = cplType.lower()
if cplType == "pdf":
returnCode = self._compilePdfLatex(msgfileName=msgfileName, outputPath=outputPath)
elif cplType == "png":
returnCode = self._compileDVIPNG(msgfileName=msgfileName, outputPath=outputPath)
elif cplType == "ps":
raise NotImplementedError("compileLatexFile: option 'ps' not yet implemented")
elif cplType == "pspdf":
raise NotImplementedError("compileLatexFile: option 'pspdf' not yet implemented")
else:
raise RuntimeError(f"Unknown compilation type '{cplType}'")
return returnCode