@@ -457,9 +457,9 @@ def format(
457
457
precision : int | None = None ,
458
458
decimal : str = "." ,
459
459
thousands : str | None = None ,
460
- escape : bool = False ,
460
+ escape : str | None = None ,
461
461
) -> StylerRenderer :
462
- """
462
+ r """
463
463
Format the text display value of cells.
464
464
465
465
Parameters
@@ -492,9 +492,13 @@ def format(
492
492
493
493
.. versionadded:: 1.3.0
494
494
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``.
498
502
499
503
.. versionadded:: 1.3.0
500
504
@@ -571,13 +575,26 @@ def format(
571
575
Using a ``formatter`` with HTML ``escape`` and ``na_rep``.
572
576
573
577
>>> 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
+ ... )
575
581
>>> s.render()
576
582
...
577
583
<td .. ><a href="a.com/<div></div>"><div></div></a></td>
578
584
<td .. ><a href="a.com/"A&B"">"A&B"</a></td>
579
585
<td .. >NA</td>
580
586
...
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}
581
598
"""
582
599
if all (
583
600
(
@@ -587,7 +604,7 @@ def format(
587
604
decimal == "." ,
588
605
thousands is None ,
589
606
na_rep is None ,
590
- escape is False ,
607
+ escape is None ,
591
608
)
592
609
):
593
610
self ._display_funcs .clear ()
@@ -771,10 +788,17 @@ def wrapper(x):
771
788
return wrapper
772
789
773
790
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"""
776
793
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
+ )
778
802
return x
779
803
780
804
@@ -784,7 +808,7 @@ def _maybe_wrap_formatter(
784
808
precision : int | None = None ,
785
809
decimal : str = "." ,
786
810
thousands : str | None = None ,
787
- escape : bool = False ,
811
+ escape : str | None = None ,
788
812
) -> Callable :
789
813
"""
790
814
Allows formatters to be expressed as str, callable or None, where None returns
@@ -804,9 +828,9 @@ def _maybe_wrap_formatter(
804
828
else :
805
829
raise TypeError (f"'formatter' expected str or callable, got { type (formatter )} " )
806
830
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 ))
810
834
else :
811
835
func_1 = func_0
812
836
@@ -1187,3 +1211,38 @@ def _parse_latex_options_strip(value: str | int | float, arg: str) -> str:
1187
1211
For example: 'red /* --wrap */ ' --> 'red'
1188
1212
"""
1189
1213
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