"""The latex text format driver."""
from io import TextIOBase
from typing import Final
from moptipy.utils.formatted_string import (
NAN,
NEGATIVE_INFINITY,
NUMBER,
POSITIVE_INFINITY,
SPECIAL,
TEXT,
)
from moptipy.utils.text_format import TextFormatDriver
#: the exponent prefix
_EPREFIX = r"\hspace*{0.15em}*\hspace*{0.1em}10\textsuperscript{"
#: special characters in LaTeX
SPECIAL_CHARS: Final[dict[str, str]] = {
"\u2205": r"$\emptyset$",
"\u221E": r"$\infty$",
"-\u221E": r"$-\infty$",
"inf": r"$\infty$",
"-inf": r"$-\infty$",
"nan": r"$\emptyset$",
"\u03b1": r"$\alpha$",
"\u2014": "---",
}
[docs]
class LaTeX(TextFormatDriver):
r"""
The LaTeX text driver.
>>> from io import StringIO
>>> from moptipy.utils.formatted_string import FormattedStr
>>> from moptipy.utils.table import Table
>>> s = StringIO()
>>> latex = LaTeX.instance()
>>> print(str(latex))
tex
>>> with Table(s, "lrc", latex) as t:
... with t.header() as header:
... with header.row() as h:
... h.cell(FormattedStr("1", bold=True))
... h.cell(FormattedStr("2", code=True))
... h.cell(FormattedStr("3", italic=True))
... with t.section() as g:
... with g.row() as r:
... r.cell("a")
... r.cell("b")
... r.cell("c")
... with g.row() as r:
... r.cell("d")
... r.cell("e")
... r.cell("f")
>>> print(f"'{s.getvalue()}'")
'\begin{tabular}{lrc}%
\hline%
{\textbf{1}}&{\texttt{2}}&{\textit{3}}\\%
\hline%
a&b&c\\%
d&e&f\\%
\hline%
\end{tabular}%
'
"""
[docs]
def begin_table_body(self, stream: TextIOBase, cols: str) -> None:
"""Write the beginning of the table body."""
stream.write(f"\\begin{{tabular}}{{{cols}}}%\n")
[docs]
def end_table_body(self, stream: TextIOBase, cols: str) -> None:
"""Write the ending of the table body."""
stream.write("\\end{tabular}%\n")
[docs]
def end_table_section(self, stream: TextIOBase, cols: str,
section_index: int, n_rows: int) -> None:
"""End a table section."""
stream.write("\\hline%\n")
[docs]
def end_table_row(self, stream: TextIOBase, cols: str,
section_index: int, row_index: int) -> None:
"""End a row in a LaTeX table."""
stream.write("\\\\%\n")
[docs]
def begin_table_cell(self, stream: TextIOBase, cols: str,
section_index: int, row_index: int,
col_index: int, cell_mode: int) -> None:
"""Begin a LaTeX table cell."""
if col_index > 0:
stream.write("&")
[docs]
def text(self, stream: TextIOBase, text: str, bold: bool, italic: bool,
code: bool, mode: int) -> None:
"""Print a text string."""
if len(text) <= 0:
return
if bold:
stream.write("{\\textbf{")
if italic:
stream.write("{\\textit{")
if code:
stream.write("{\\texttt{")
if mode == TEXT:
stream.write(text.replace("_", "\\_"))
elif mode == NUMBER:
i: int = text.find("e")
if i < 0:
i = text.find("E")
if i > 0:
stream.write(f"{text[:i]}{_EPREFIX}{text[i + 1:]}}}")
else:
stream.write(text.replace("_", "\\_"))
elif mode == NAN:
stream.write(r"$\emptyset$")
elif mode == POSITIVE_INFINITY:
stream.write(r"$\infty$")
elif mode == NEGATIVE_INFINITY:
stream.write(r"$-\infty")
elif mode == SPECIAL:
s: Final[str] = str(text)
if s not in SPECIAL_CHARS:
raise ValueError(f"invalid special character: {s!r}")
stream.write(SPECIAL_CHARS[s])
else:
raise ValueError(f"invalid mode {mode} for text {text!r}.")
if code:
stream.write("}}")
if italic:
stream.write("}}")
if bold:
stream.write("}}")
def __str__(self):
"""
Get the appropriate file suffix.
:returns: the file suffix
:retval 'tex': always
"""
return "tex"
[docs]
@staticmethod
def instance() -> "LaTeX":
"""
Get the LaTeX format singleton instance.
:returns: the singleton instance of the LaTeX format
"""
attr: Final[str] = "_instance"
func: Final = LaTeX.instance
if not hasattr(func, attr):
setattr(func, attr, LaTeX())
return getattr(func, attr)