diff --git a/great_tables/_formats.py b/great_tables/_formats.py index 5d6e8e858..0b97b4d20 100644 --- a/great_tables/_formats.py +++ b/great_tables/_formats.py @@ -17,7 +17,7 @@ TypeVar, Union, cast, - overload, + overload ) import babel @@ -25,7 +25,8 @@ from babel.dates import format_date, format_datetime, format_time from typing_extensions import TypeAlias -from ._gt_data import FormatFn, FormatFns, FormatInfo, FormatterSkipElement, GTData, PFrameData +from ._gt_data import (FormatFn, FormatFns, FormatInfo, FormatterSkipElement, + GTData, PFrameData) from ._helpers import px from ._locale import ( _get_currencies_data, @@ -44,7 +45,7 @@ is_series, to_list, ) -from ._text import _md_html, escape_pattern_str_latex +from ._text import _md_html, _md_latex, escape_pattern_str_latex from ._utils import _str_detect, _str_replace, is_valid_http_schema from ._utils_nanoplots import _generate_nanoplot @@ -2668,7 +2669,7 @@ def fmt_markdown_context( context: str, ) -> str: if context == "latex": - raise NotImplementedError("fmt_markdown() is not supported in LaTeX.") + return _md_latex(x) if is_na(data._tbl_data, x): return x diff --git a/great_tables/_text.py b/great_tables/_text.py index cd895ec70..7a62c06d8 100644 --- a/great_tables/_text.py +++ b/great_tables/_text.py @@ -6,6 +6,9 @@ from typing import Callable import commonmark +from docutils.core import publish_parts +from markdownify import markdownify as mdify +from myst_parser.docutils_ import Parser class BaseText: @@ -48,13 +51,7 @@ def to_html(self) -> str: return self.text def to_latex(self) -> str: - from ._utils_render_latex import _not_implemented - - _not_implemented( - "Using the `html()` helper function won't convert HTML to LaTeX. Escaping HTML string instead." - ) - - return _latex_escape(self.text) + return _html_latex(self.text) def _md_html(x: str) -> str: @@ -63,10 +60,34 @@ def _md_html(x: str) -> str: def _md_latex(x: str) -> str: - # TODO: Implement commonmark to LaTeX conversion (through a different library as - # commonmark-py does not support it) - raise NotImplementedError("Markdown to LaTeX conversion is not supported yet") - + # USE REGEX TO CONVERT AND TO MYST MARKDOWN + input = re.sub(r'(.*?)', r'{sub}`\1`', x) + input = re.sub(r'(.*?)', r'{sup}`\1`', input) + + # Use Myst-Parser to convert Markdown to LaTeX + raw_output = publish_parts( + source=input, + writer_name="latex", + settings_overrides={ + "myst_enable_extensions": ['strikethrough'], + "embed_stylesheet": False, + "legacy_column_widths": True, + "use_latex_citations": False, + }, + parser=Parser(), + ) + + output = raw_output['body'].strip() + + return output + + +def _html_latex(x: str) -> str: + # Turn HTML to Markdown first + input = mdify(x, strip=['br'], sub_symbol="", sup_symbol="") + + # Then render Markdown to LaTeX + return _md_latex(input) def _process_text(x: str | BaseText | None, context: str = "html") -> str: if x is None: diff --git a/pyproject.toml b/pyproject.toml index bb43b2c1f..1985a2df2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,7 +44,9 @@ dependencies = [ "importlib-metadata", "typing_extensions>=3.10.0.0", "Babel>=2.13.1", - "importlib-resources" + "importlib-resources", + "markdownify>=1.2.2", + "myst_parser>=4.0.1" ] requires-python = ">=3.9" diff --git a/tests/test_text.py b/tests/test_text.py index 9d2f53d6c..3e230c64e 100644 --- a/tests/test_text.py +++ b/tests/test_text.py @@ -8,6 +8,7 @@ _latex_escape, escape_pattern_str_latex, _process_text, + _md_latex, ) @@ -30,7 +31,7 @@ def test_md_class(): def test_html_class(): assert Html("text").to_html() == "text" - assert Html("text").to_latex() == "text" + assert Html("text").to_latex() == "\\textbf{text}" def test_latex_escape(): @@ -56,17 +57,17 @@ def test_process_text_html(): def test_process_text_latex(): assert _process_text("a & _b_", context="latex") == "a \\& \\_b\\_" assert _process_text(Text("\\_\\$"), context="latex") == "\\_\\$" - assert _process_text(Html("**a** & "), context="latex") == "**a** \\& " + assert _process_text(Html("**a** & bold"), context="latex") == "**a** \\& \\textbf{bold}" + assert _process_text(Md("**a**"), context="latex") == "\\textbf{a}" assert _process_text(None, context="latex") == "" - with pytest.raises(NotImplementedError) as exc_info: - _process_text(Md("**a** & "), context="latex") - - assert "Markdown to LaTeX conversion is not supported yet" in exc_info.value.args[0] - def test_process_text_raises(): with pytest.raises(TypeError) as exc_info: _process_text(1, context="html") # type: ignore assert "Invalid type: " in exc_info.value.args[0] + + +def test_md_latex(): + assert _md_latex("Testing **bold** text") == "Testing \\textbf{bold} text" \ No newline at end of file