Source code for mojo.latex.pyTex

# -*- 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
[docs] def addFigure(self, fileName, position="ht", width="\\textwidth", height="", furtherSettings="", caption="", label="", noNumbering=False, exists=True, appendix=False): """add figure environment to content **parameters**: * **fileName** - filename of the graphics file **optional parameters**: * **position** - position of figure in document * **width** - width of figure * **height** - height of figure * **caption** - caption of table * **label** - corresponding label * **noNumbering** - flag to indicate if labeling is wanted * **exists** - flag if file exists * **appendix** - flag to add this entry to the appendix instead of the main content """ figTable = [] figTable.append(f"\\begin{{figure}}[{position}]") figTable.append("\\begin{center}") if exists: figSize = [] if width: figSize.append(f"width = {width}") if height: figSize.append(f"height = {height}") if furtherSettings: figSize.append(furtherSettings) if figSize: figSizeEntry = "[%s]" % (", ".join(figSize)) else: figSizeEntry = "" base, extension = os.path.splitext(fileName) figTable.append('\\includegraphics%s{%s%s}' % (figSizeEntry, base, extension)) else: figTable.append('\\color{red}') figTable.append('\\begin{verbatim}') figTable.append(' ' + '!!' * 38) figTable.append(' ' + '!!' * 38) figTable.append(' !!!!' + ' ' * 34 + '!!!!') figTable.append(' !!!! Missing image file: %s !!!!' % fileName.ljust(45)) figTable.append(' !!!!' + ' ' * 34 + '!!!!') figTable.append(' ' + '!!' * 38) figTable.append(' ' + '!!' * 38) figTable.append('\\end{verbatim}') if caption: figTable.append(f'\\caption{{{caption}}}') if not noNumbering: labeling = _labeling(label, 1, nType=self.numberOfFigures) figTable.append(labeling) figTable.append('\\end{center}') figTable.append('\\end{figure}\n') self.__getContent(appendix).append("\n".join(figTable)) self.numberOfFigures += 1
# add subigure environment
[docs] def addSubFigure(self, fileList, captionList, position="ht", widthSubfigure="0.45\\textwidth", appendix=False): """ add subfigure to content :param fileList: names of subfigures :param captionList: names of desired captions - pay attention: that first entry is always the main caption for entire figure :param position: position of the figure in document :param widthSubfigure: defines the size of each subfigure :param appendix: flag to add this entry to the appendix instead of the main content (optional, default: False) :type fileList: list of strings :type captionList: list of strings :type position: string :type widthSubfigure: string :type appendix: boolean """ subfigList = [] if (len(fileList) + 1) != len(captionList): raise ValueError("Number of defined files for subfigures and associated captions do not match.") subfigList.append("\\begin{figure}[%s]" % position) for figNumber, fileName in enumerate(fileList): subfigList.append("\\begin{subfigure}[c]{%s}" % widthSubfigure) subfigList.append("\\includegraphics[width = 0.95\\textwidth]{{{%s}}}" % fileName) subfigList.append("\\subcaption{%s}" % captionList[figNumber + 1]) subfigList.append("\\end{subfigure}") subfigList.append("\\caption{%s}" % captionList[0]) subfigList.append("\\end{figure}") self.__getContent(appendix).append("\n".join(subfigList)) self.numberOfFigures += 1
[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