@@ -457,9 +457,9 @@ def format(
457457 precision : int | None = None ,
458458 decimal : str = "." ,
459459 thousands : str | None = None ,
460- escape : bool = False ,
460+ escape : str | None = None ,
461461 ) -> StylerRenderer :
462- """
462+ r """
463463 Format the text display value of cells.
464464
465465 Parameters
@@ -492,9 +492,13 @@ def format(
492492
493493 .. versionadded:: 1.3.0
494494
495- escape : bool, default False
496- Replace the characters ``&``, ``<``, ``>``, ``'``, and ``"`` in cell display
497- string with HTML-safe sequences. Escaping is done before ``formatter``.
495+ escape : str, optional
496+ Use 'html' to replace the characters ``&``, ``<``, ``>``, ``'``, and ``"``
497+ in cell display string with HTML-safe sequences.
498+ Use 'latex' to replace the characters ``&``, ``%``, ``$``, ``#``, ``_``,
499+ ``{``, ``}``, ``~``, ``^``, and ``\`` in the cell display string with
500+ LaTeX-safe sequences.
501+ Escaping is done before ``formatter``.
498502
499503 .. versionadded:: 1.3.0
500504
@@ -571,13 +575,26 @@ def format(
571575 Using a ``formatter`` with HTML ``escape`` and ``na_rep``.
572576
573577 >>> df = pd.DataFrame([['<div></div>', '"A&B"', None]])
574- >>> s = df.style.format('<a href="a.com/{0}">{0}</a>', escape=True, na_rep="NA")
578+ >>> s = df.style.format(
579+ ... '<a href="a.com/{0}">{0}</a>', escape="html", na_rep="NA"
580+ ... )
575581 >>> s.render()
576582 ...
577583 <td .. ><a href="a.com/<div></div>"><div></div></a></td>
578584 <td .. ><a href="a.com/"A&B"">"A&B"</a></td>
579585 <td .. >NA</td>
580586 ...
587+
588+ Using a ``formatter`` with LaTeX ``escape``.
589+
590+ >>> df = pd.DataFrame([["123"], ["~ ^"], ["$%#"]])
591+ >>> s = df.style.format("\\textbf{{{}}}", escape="latex").to_latex()
592+ \begin{tabular}{ll}
593+ {} & {0} \\
594+ 0 & \textbf{123} \\
595+ 1 & \textbf{\textasciitilde \space \textasciicircum } \\
596+ 2 & \textbf{\$\%\#} \\
597+ \end{tabular}
581598 """
582599 if all (
583600 (
@@ -587,7 +604,7 @@ def format(
587604 decimal == "." ,
588605 thousands is None ,
589606 na_rep is None ,
590- escape is False ,
607+ escape is None ,
591608 )
592609 ):
593610 self ._display_funcs .clear ()
@@ -771,10 +788,17 @@ def wrapper(x):
771788 return wrapper
772789
773790
774- def _str_escape_html ( x ):
775- """if escaping html : only use on str, else return input"""
791+ def _str_escape ( x , escape ):
792+ """if escaping: only use on str, else return input"""
776793 if isinstance (x , str ):
777- return escape_html (x )
794+ if escape == "html" :
795+ return escape_html (x )
796+ elif escape == "latex" :
797+ return _escape_latex (x )
798+ else :
799+ raise ValueError (
800+ f"`escape` only permitted in {{'html', 'latex'}}, got { escape } "
801+ )
778802 return x
779803
780804
@@ -784,7 +808,7 @@ def _maybe_wrap_formatter(
784808 precision : int | None = None ,
785809 decimal : str = "." ,
786810 thousands : str | None = None ,
787- escape : bool = False ,
811+ escape : str | None = None ,
788812) -> Callable :
789813 """
790814 Allows formatters to be expressed as str, callable or None, where None returns
@@ -804,9 +828,9 @@ def _maybe_wrap_formatter(
804828 else :
805829 raise TypeError (f"'formatter' expected str or callable, got { type (formatter )} " )
806830
807- # Replace HTML chars if escaping
808- if escape :
809- func_1 = lambda x : func_0 (_str_escape_html ( x ))
831+ # Replace chars if escaping
832+ if escape is not None :
833+ func_1 = lambda x : func_0 (_str_escape ( x , escape = escape ))
810834 else :
811835 func_1 = func_0
812836
@@ -1187,3 +1211,38 @@ def _parse_latex_options_strip(value: str | int | float, arg: str) -> str:
11871211 For example: 'red /* --wrap */ ' --> 'red'
11881212 """
11891213 return str (value ).replace (arg , "" ).replace ("/*" , "" ).replace ("*/" , "" ).strip ()
1214+
1215+
1216+ def _escape_latex (s ):
1217+ r"""
1218+ Replace the characters ``&``, ``%``, ``$``, ``#``, ``_``, ``{``, ``}``,
1219+ ``~``, ``^``, and ``\`` in the string with LaTeX-safe sequences.
1220+
1221+ Use this if you need to display text that might contain such characters in LaTeX.
1222+
1223+ Parameters
1224+ ----------
1225+ s : str
1226+ Input to be escaped
1227+
1228+ Return
1229+ ------
1230+ str :
1231+ Escaped string
1232+ """
1233+ return (
1234+ s .replace ("\\ " , "ab2§=§8yz" ) # rare string for final conversion: avoid \\ clash
1235+ .replace ("ab2§=§8yz " , "ab2§=§8yz\\ space " ) # since \backslash gobbles spaces
1236+ .replace ("&" , "\\ &" )
1237+ .replace ("%" , "\\ %" )
1238+ .replace ("$" , "\\ $" )
1239+ .replace ("#" , "\\ #" )
1240+ .replace ("_" , "\\ _" )
1241+ .replace ("{" , "\\ {" )
1242+ .replace ("}" , "\\ }" )
1243+ .replace ("~ " , "~\\ space " ) # since \textasciitilde gobbles spaces
1244+ .replace ("~" , "\\ textasciitilde " )
1245+ .replace ("^ " , "^\\ space " ) # since \textasciicircum gobbles spaces
1246+ .replace ("^" , "\\ textasciicircum " )
1247+ .replace ("ab2§=§8yz" , "\\ textbackslash " )
1248+ )
0 commit comments