From 0f476daf16fbe21959d10571d342178e84cd5db7 Mon Sep 17 00:00:00 2001 From: Lorenzo Vannucci Date: Thu, 21 Oct 2021 18:19:02 +0200 Subject: [PATCH] Added function to directly get the format from LaTeX --- docs/source/getting_started.rst | 3 + docs/source/matplotlib_latex_bridge.rst | 6 ++ src/matplotlib_latex_bridge/__init__.py | 3 +- .../matplotlib_latex_bridge.py | 102 ++++++++++++++++++ tests/test_functions.py | 14 +++ 5 files changed, 127 insertions(+), 1 deletion(-) diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index 576ff40..e3c36ff 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -82,6 +82,9 @@ specified with the ``documentclass`` command, like in \documentclass[letterpaper, 10pt]{article} +All these values can be gathered directly from the LaTeX processor, using +:func:`matplotlib_latex_bridge.get_format_from_latex`, if a working LaTeX installation is present. + A more fine-grained control over the font sizes can be achieved by using :func:`matplotlib_latex_bridge.set_font_sizes` and :func:`matplotlib_latex_bridge.set_font_family`. diff --git a/docs/source/matplotlib_latex_bridge.rst b/docs/source/matplotlib_latex_bridge.rst index ad58011..044baed 100644 --- a/docs/source/matplotlib_latex_bridge.rst +++ b/docs/source/matplotlib_latex_bridge.rst @@ -36,3 +36,9 @@ in the initial setup. .. autofunction:: figure_textwidth .. autofunction:: figure + +Getting format from LaTeX +------------------------- +This function can be used to get format informations directly from LaTeX, but requires a working LaTeX installation. + +.. autofunction:: get_format_from_latex \ No newline at end of file diff --git a/src/matplotlib_latex_bridge/__init__.py b/src/matplotlib_latex_bridge/__init__.py index a49caa7..b4ff689 100644 --- a/src/matplotlib_latex_bridge/__init__.py +++ b/src/matplotlib_latex_bridge/__init__.py @@ -3,7 +3,8 @@ from .matplotlib_latex_bridge import setup_page,\ set_font_sizes, set_font_family,\ set_default_figsize, get_default_figsize,\ - figure_columnwidth, figure_textwidth, figure + figure_columnwidth, figure_textwidth, figure, \ + get_format_from_latex import matplotlib_latex_bridge.formats diff --git a/src/matplotlib_latex_bridge/matplotlib_latex_bridge.py b/src/matplotlib_latex_bridge/matplotlib_latex_bridge.py index e360cbc..6388ffc 100644 --- a/src/matplotlib_latex_bridge/matplotlib_latex_bridge.py +++ b/src/matplotlib_latex_bridge/matplotlib_latex_bridge.py @@ -2,6 +2,10 @@ import matplotlib.pyplot as plt import os import sys +import re +import subprocess +import tempfile +import shutil mlb_initialized = False @@ -225,3 +229,101 @@ def figure(width=None, height=None, **kwargs): print("Requested width ({}) is larger that textwidth ({})".format(w, mlb_textwidth), file=sys.stderr) return plt.figure(figsize=(w, h), **kwargs) + + +def get_format_from_latex(documentclass, columns=None, papersize=None, fontsize=None, otheroptions=None): + """ + Get the format by invoking the LaTeX processor + + This functions compiles a sample file with the LaTeX processor and parse its output to get information about + text and column widths and fontsize. + + The output of this function can be directly used to setup the page. + + Using this function requires a working LaTeX installation. + + :param documentclass: layout standard to use (ex. article, report, book, ...) + :param columns: number of columns (ex. twocolumn) + :param papersize: size of the paper (ex. a4paper, letterpaper, ...) + :param fontsize: size of the font (ex. 10pt, 11pt, 12pt) + :param otheroptions: comma-separated additional options + :return: dictionary with textwidth, columnwidth and fontsize + """ + + # check for LaTeX + haslatex = any((os.access(os.path.join(path, "latex"), os.X_OK) and os.path.isfile(os.path.join(path, "latex"))) + for path in os.environ["PATH"].split(os.pathsep)) + + if not haslatex: + raise RuntimeError("No LaTeX installation found") + + # build file content + options = "" + if columns: + options = options + str(columns) + "," + if papersize: + options = options + str(papersize) + "," + if fontsize: + if type(fontsize) != str: + options = options + str(fontsize) + "pt," + else: + options = options + fontsize + "," + if otheroptions: + options = otheroptions + otheroptions + latex_file_content = r"\documentclass[{options}]{{{documentclass}}}".format(documentclass=documentclass, + options=options) + \ + r""" + +\usepackage{layouts} + +\begin{document} + +textwidth: \printinunitsof{in}\prntlen{\textwidth} +\message{textwidth: \prntlen{\textwidth}^^J} + +columnwidth: \printinunitsof{in}\prntlen{\columnwidth} +\message{columnwidth: \prntlen{\columnwidth}^^J} + +\message{fontsize: \the\font^^J} + +\end{document} +""" + + # create temporary directory to run latex + tmpdir = tempfile.mkdtemp() + + # write latex file + with open(tmpdir + "/file.tex", "w") as texfile: + texfile.write(latex_file_content) + + # run latex + latex_output = subprocess.check_output(["latex", "file.tex"], cwd=tmpdir).decode() + + shutil.rmtree(tmpdir) + + latex_output = latex_output.replace("\n", "") + + # find widths + twr = re.compile(r"\\relax ([0-9]+\.[0-9]+)in") + m = twr.findall(latex_output) + + if len(m) != 2: + raise RuntimeError("Something went wrong with the execution of LaTeX") + + textwidth = float(m[0]) + columnwidth = float(m[1]) + + # find font size + fr = re.compile(r"fontsize:.*/([0-9]+\.?[0-9]*)") + m = fr.search(latex_output) + + if m is None: + raise RuntimeError("Something went wrong with the execution of LaTeX") + + fontsize = float(m.groups()[0]) + + return { + "textwidth": textwidth, + "columnwidth": columnwidth, + "fontsize": fontsize + } diff --git a/tests/test_functions.py b/tests/test_functions.py index 52b7898..7009766 100644 --- a/tests/test_functions.py +++ b/tests/test_functions.py @@ -69,5 +69,19 @@ def test_width_percentage_error(self, mock_stderr): self.assertIn("Invalid percentual width", mock_stderr.getvalue()) +class TestLatex(unittest.TestCase): + + def test_format_from_latex(self): + + fmt = mlb.get_format_from_latex(documentclass="article", + columns="twocolumn", + papersize="letterpaper", + fontsize=12) + + self.assertAlmostEqual(fmt["textwidth"], 6.49083) + self.assertAlmostEqual(fmt["columnwidth"], 3.17621) + self.assertAlmostEqual(fmt["fontsize"], 12) + + if __name__ == '__main__': unittest.main()