diff options
Diffstat (limited to 'pint')
-rw-r--r-- | pint/formatting.py | 19 | ||||
-rw-r--r-- | pint/testsuite/test_unit.py | 27 |
2 files changed, 45 insertions, 1 deletions
diff --git a/pint/formatting.py b/pint/formatting.py index 554b381..f450d5f 100644 --- a/pint/formatting.py +++ b/pint/formatting.py @@ -10,6 +10,7 @@ from __future__ import annotations +import functools import re import warnings from typing import Callable, Dict @@ -178,10 +179,26 @@ def format_pretty(unit, registry, **options): ) +def latex_escape(string): + """ + Prepend characters that have a special meaning in LaTeX with a backslash. + """ + return functools.reduce( + lambda s, m: re.sub(m[0], m[1], s), + ( + (r"[\\]", r"\\textbackslash "), + (r"[~]", r"\\textasciitilde "), + (r"[\^]", r"\\textasciicircum "), + (r"([&%$#_{}])", r"\\\1"), + ), + str(string), + ) + + @register_unit_format("L") def format_latex(unit, registry, **options): preprocessed = { - r"\mathrm{{{}}}".format(u.replace("_", r"\_")): p for u, p in unit.items() + r"\mathrm{{{}}}".format(latex_escape(u)): p for u, p in unit.items() } formatted = formatter( preprocessed.items(), diff --git a/pint/testsuite/test_unit.py b/pint/testsuite/test_unit.py index 96db871..402556c 100644 --- a/pint/testsuite/test_unit.py +++ b/pint/testsuite/test_unit.py @@ -54,6 +54,33 @@ class TestUnit(QuantityTestCase): with subtests.test(spec): assert spec.format(x) == result + def test_latex_escaping(self, subtests): + ureg = UnitRegistry() + ureg.define(r"percent = 1e-2 = %") + x = ureg.Unit(UnitsContainer(percent=1)) + for spec, result in { + "L": r"\mathrm{percent}", + "L~": r"\mathrm{\%}", + "Lx": r"\si[]{\percent}", + "Lx~": r"\si[]{\%}", + }.items(): + with subtests.test(spec): + ureg.default_format = spec + assert f"{x}" == result, f"Failed for {spec}, {result}" + # no '#' here as it's a comment char when define()ing new units + ureg.define(r"weirdunit = 1 = \~_^&%$_{}") + x = ureg.Unit(UnitsContainer(weirdunit=1)) + for spec, result in { + "L": r"\mathrm{weirdunit}", + "L~": r"\mathrm{\textbackslash \textasciitilde \_\textasciicircum \&\%\$\_\{\}}", + "Lx": r"\si[]{\weirdunit}", + # TODO: Currently makes \si[]{\\~_^&%$_{}} (broken). What do we even want this to be? + # "Lx~": r"\si[]{\textbackslash \textasciitilde \_\textasciicircum \&\%\$\_\{\}}", + }.items(): + with subtests.test(spec): + ureg.default_format = spec + assert f"{x}" == result, f"Failed for {spec}, {result}" + def test_unit_default_formatting(self, subtests): ureg = UnitRegistry() x = ureg.Unit(UnitsContainer(meter=2, kilogram=1, second=-1)) |