@@ -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