Skip to content

ENH: Styler.to_latex conversion from CSS #40731

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 150 commits into from
Jun 16, 2021
Merged
Show file tree
Hide file tree
Changes from 143 commits
Commits
Show all changes
150 commits
Select commit Hold shift + click to select a range
bb501d3
MVP for Styler.to_latex
attack68 Mar 10, 2021
6066449
MVP for Styler.to_latex
attack68 Mar 10, 2021
6e2b788
rules
attack68 Mar 11, 2021
2f74690
hidden columns and index
attack68 Mar 11, 2021
9c9405e
basic cell colors and background colors parsing.
attack68 Mar 11, 2021
a6772e7
basic cell colors and background colors parsing.
attack68 Mar 11, 2021
6505564
basic cell colors and background colors parsing.
attack68 Mar 11, 2021
b25644f
column_format arg input
attack68 Mar 11, 2021
2ba7e02
add file buffer
attack68 Mar 11, 2021
79b978b
optional table wrapping
attack68 Mar 11, 2021
ed8b405
refactor
attack68 Mar 11, 2021
f7d52f4
more flexible table_styles
attack68 Mar 11, 2021
f35c7e1
docs to parse_latex
attack68 Mar 12, 2021
521aee2
auto column_format for numerics
attack68 Mar 13, 2021
849656f
to_latex unit tests
attack68 Mar 13, 2021
cce7285
more unit tests and code bugs removed
attack68 Mar 13, 2021
26edba4
deal with multi col headers
attack68 Mar 13, 2021
3d7c614
deal with sparsification with multirow and multicol
attack68 Mar 13, 2021
e2f0f0c
tests for multirow and multicol
attack68 Mar 13, 2021
3243ed7
comprehensive test
attack68 Mar 13, 2021
0decf2d
refactor
attack68 Mar 13, 2021
c8dc5e5
change -wrap- to --wrap
attack68 Mar 13, 2021
8c58538
remove redundant comments
attack68 Mar 13, 2021
5c9d50a
docs
attack68 Mar 13, 2021
563aef6
docs
attack68 Mar 13, 2021
99abe1c
doc edits
attack68 Mar 14, 2021
5185f27
mypy fix
attack68 Mar 14, 2021
a824f13
docs
attack68 Mar 14, 2021
f6211e8
Merge remote-tracking branch 'upstream/master' into latex_styler_mvp
attack68 Mar 14, 2021
b46a9af
docs
attack68 Mar 14, 2021
7e3c5e1
docs
attack68 Mar 14, 2021
80d3cac
docs
attack68 Mar 14, 2021
9388dec
docs
attack68 Mar 14, 2021
ccac263
check fix
attack68 Mar 14, 2021
de69c21
col format for basic render
attack68 Mar 14, 2021
05f5b34
dependency fix
attack68 Mar 14, 2021
4d98615
Merge remote-tracking branch 'upstream/master' into latex_styler_mvp
attack68 Mar 15, 2021
f036d25
test for multicols and hidden cols combined
attack68 Mar 15, 2021
a9e0ce4
test for hidden columns in a multiindex
attack68 Mar 17, 2021
989278f
better test
attack68 Mar 17, 2021
3a79966
better test
attack68 Mar 17, 2021
9051bfe
Merge remote-tracking branch 'upstream/master' into latex_styler_mvp
attack68 Mar 21, 2021
938a0e8
fix latex for " "
attack68 Mar 21, 2021
0aed431
Merge remote-tracking branch 'upstream/master' into latex_styler_mvp
attack68 Mar 24, 2021
0c0e2bc
latex CSS conversion
attack68 Mar 19, 2021
e82b566
colors as HTML and proper RGB parsing
attack68 Mar 20, 2021
095af35
add extended wrapping flags, for potential future dev
attack68 Mar 28, 2021
8af3c81
enhance the wrapping arguments
attack68 Mar 29, 2021
2e0aee6
remove render(latex=True) option
attack68 Mar 29, 2021
5067ffc
remove render(latex=True) option tests
attack68 Mar 29, 2021
ff4954e
remove render latex
attack68 Mar 29, 2021
d9836aa
remove render latex
attack68 Mar 29, 2021
5bdcb4e
put column headers in braces if not multicol
attack68 Mar 30, 2021
6f4a44b
add siunitx option, and expand docs
attack68 Mar 30, 2021
c211480
add sparsify as input argument and tests
attack68 Mar 30, 2021
2d32556
multirow and multicol alignment options
attack68 Mar 30, 2021
816e62f
test hidden index
attack68 Mar 30, 2021
9c577ce
change 'float' to 'position_float' to avoid shadow name
attack68 Mar 30, 2021
74721a8
doc internals
attack68 Mar 30, 2021
a6bc485
merge with latex mvp
attack68 Mar 30, 2021
7c7f0de
change default wrap
attack68 Mar 30, 2021
527724d
Merge branch 'latex_styler_mvp' into latex_css_conversion
attack68 Mar 30, 2021
4cdefb8
change default wrap
attack68 Mar 30, 2021
ee7397f
change test
attack68 Mar 30, 2021
f381011
Merge remote-tracking branch 'upstream/master' into latex_styler_mvp
attack68 Mar 30, 2021
eb21587
Merge branch 'latex_styler_mvp' into latex_css_conversion
attack68 Mar 30, 2021
b9ff43d
documentation
attack68 Mar 30, 2021
2f5cdec
documentation
attack68 Mar 31, 2021
a6e4e47
compress code
attack68 Mar 31, 2021
e385fb0
clarify code
attack68 Mar 31, 2021
4eacb12
clarify code
attack68 Mar 31, 2021
b71d9eb
Merge branch 'latex_styler_mvp' into latex_css_conversion
attack68 Mar 31, 2021
fd95e34
simplify code
attack68 Apr 1, 2021
7126bda
simplify code
attack68 Apr 1, 2021
25bcba9
simplify code
attack68 Apr 1, 2021
34f0b91
Merge branch 'latex_styler_mvp' into latex_css_conversion
attack68 Apr 1, 2021
e35eea7
user guide docs
attack68 Apr 1, 2021
5ec08d8
basic subclassing
attack68 Apr 2, 2021
b74057b
basic subclassing
attack68 Apr 2, 2021
a49deeb
non_reducing_slice deprivatized
attack68 Apr 2, 2021
228f146
move io methods
attack68 Apr 2, 2021
44ead2d
import typing aliases
attack68 Apr 2, 2021
0ae8c39
rename renderer
attack68 Apr 2, 2021
70067c7
move to_excel and _repr_html_ back
attack68 Apr 2, 2021
3772cc8
move to_excel and _repr_html_ back
attack68 Apr 2, 2021
19828c3
Merge remote-tracking branch 'upstream/master' into basic_subclassing…
attack68 Apr 2, 2021
79ad3a6
Merge remote-tracking branch 'upstream/master' into basic_subclassing…
attack68 Apr 6, 2021
03415e5
fix typing to new standard
attack68 Apr 7, 2021
5697ff9
Merge remote-tracking branch 'upstream/master' into basic_subclassing…
attack68 Apr 9, 2021
41760e0
merge upstream master
attack68 Apr 10, 2021
92c11d1
Merge remote-tracking branch 'upstream/master' into basic_subclassing…
attack68 Apr 10, 2021
6547c6d
Merge branch 'basic_subclassing_styler' into latex_styler_mvp
attack68 Apr 10, 2021
3910fcf
Merge branch 'basic_subclassing_styler' into latex_styler_mvp
attack68 Apr 10, 2021
0164d90
documentation
attack68 Apr 11, 2021
463a54b
isort fix
attack68 Apr 11, 2021
4b7298f
doc improvements
attack68 Apr 11, 2021
47a31a9
doc improvements
attack68 Apr 11, 2021
c373bb6
Merge remote-tracking branch 'upstream/master' into latex_styler_mvp
attack68 Apr 12, 2021
8b00376
doc improvements
attack68 Apr 12, 2021
b1e230d
remove column_format branch
attack68 Apr 12, 2021
fd19d97
Merge remote-tracking branch 'upstream/master' into latex_styler_mvp
attack68 Apr 12, 2021
e31dba0
Merge remote-tracking branch 'upstream/master' into latex_styler_mvp
attack68 Apr 13, 2021
bdab13d
Merge remote-tracking branch 'upstream/master' into latex_styler_mvp
attack68 Apr 13, 2021
1074b62
Merge remote-tracking branch 'upstream/master' into latex_styler_mvp
attack68 Apr 13, 2021
9e5ca75
merge
attack68 Apr 13, 2021
133c1a0
merge
attack68 Apr 13, 2021
394bc3a
Merge remote-tracking branch 'upstream/master' into latex_styler_mvp
attack68 Apr 14, 2021
034960f
doc fix
attack68 Apr 14, 2021
8b62f8d
Merge remote-tracking branch 'upstream/master' into latex_styler_mvp
attack68 Apr 14, 2021
a0f1e79
Merge remote-tracking branch 'upstream/master' into latex_styler_mvp
attack68 Apr 15, 2021
2e714e9
Merge remote-tracking branch 'upstream/master' into latex_styler_mvp
attack68 Apr 18, 2021
38c62eb
Merge remote-tracking branch 'upstream/master' into latex_styler_mvp
attack68 Apr 20, 2021
f95c21e
Merge remote-tracking branch 'upstream/master' into latex_styler_mvp
attack68 Apr 21, 2021
c925958
Merge remote-tracking branch 'upstream/master' into latex_styler_mvp
attack68 Apr 23, 2021
2cc8ad2
mutate d, so return None instead
attack68 Apr 24, 2021
7d962df
review the main to_latex docs (rhshadrach requests)
attack68 Apr 24, 2021
a6f6af2
validate position_float (rhshadrach request)
attack68 Apr 24, 2021
dce66d6
grammar fix
attack68 Apr 24, 2021
8fc9f0b
_parse_latex_table_wrapping doc and bool return (rhshadrach request)
attack68 Apr 24, 2021
bb4479b
_parse_latex_table_styles doc
attack68 Apr 24, 2021
e2ea5ae
_parse_latex_cell_styles doc chg and break (rhshadrach request)
attack68 Apr 24, 2021
518485d
fix boolean
attack68 Apr 24, 2021
d99245c
_parse_latex_header_span docs (rhshadrach request)
attack68 Apr 24, 2021
766ad49
_translate_latex docs and return (rhshadrach request)
attack68 Apr 24, 2021
10c4682
Merge branch 'latex_styler_mvp' into latex_css_conversion
attack68 Apr 24, 2021
ee92f37
Merge remote-tracking branch 'upstream/master' into latex_styler_mvp
attack68 Apr 26, 2021
c7844d5
Merge remote-tracking branch 'origin/latex_styler_mvp' into latex_css…
attack68 Apr 26, 2021
b8e3418
Merge remote-tracking branch 'upstream/master' into latex_styler_mvp
attack68 Apr 26, 2021
2aca649
Merge remote-tracking branch 'upstream/master' into latex_styler_mvp
attack68 Apr 27, 2021
cf2f1e1
Merge remote-tracking branch 'upstream/master' into latex_styler_mvp
attack68 Apr 29, 2021
8f8da7d
Merge branch 'latex_styler_mvp' into latex_css_conversion
attack68 Apr 29, 2021
0ea3127
merge upstream master
attack68 May 24, 2021
a224cdd
add conversion docs
attack68 May 24, 2021
d91f7c3
update docs
attack68 May 24, 2021
f9f6622
remove updates to user_guide
attack68 May 24, 2021
002eb90
remove updates to user_guide
attack68 May 24, 2021
372a71a
mypy fix
attack68 May 25, 2021
11985a5
Merge remote-tracking branch 'upstream/master' into latex_css_conversion
attack68 May 26, 2021
aa8e051
Merge remote-tracking branch 'upstream/master' into latex_css_conversion
attack68 May 27, 2021
1c2280d
Merge remote-tracking branch 'upstream/master' into latex_css_conversion
attack68 May 27, 2021
17f649c
Merge remote-tracking branch 'upstream/master' into latex_css_conversion
attack68 May 28, 2021
1bb35c1
Merge remote-tracking branch 'upstream/master' into latex_css_conversion
attack68 Jun 1, 2021
a55cc5b
Merge remote-tracking branch 'upstream/master' into latex_css_conversion
attack68 Jun 3, 2021
dbb76ee
Merge remote-tracking branch 'upstream/master' into latex_css_conversion
attack68 Jun 4, 2021
c569245
Merge remote-tracking branch 'upstream/master' into latex_css_conversion
attack68 Jun 5, 2021
0ac614b
whats new addition.
attack68 Jun 5, 2021
3dd649a
Merge remote-tracking branch 'upstream/master' into latex_css_conversion
attack68 Jun 9, 2021
18728c1
Merge remote-tracking branch 'upstream/master' into latex_css_conversion
attack68 Jun 11, 2021
23281b1
Merge remote-tracking branch 'upstream/master' into latex_css_conversion
attack68 Jun 12, 2021
689afc6
Merge branch 'rls1.3.0' into latex_css_conversion
attack68 Jun 15, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions pandas/io/formats/style.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ def to_latex(
multicol_align: str = "r",
siunitx: bool = False,
encoding: str | None = None,
convert_css: bool = False,
):
r"""
Write Styler to a file, buffer or string in LaTeX format.
Expand Down Expand Up @@ -480,6 +481,10 @@ def to_latex(
Set to ``True`` to structure LaTeX compatible with the {siunitx} package.
encoding : str, default "utf-8"
Character encoding setting.
convert_css : bool, default False
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need an option for this? why wouldn't we always do this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We wouldn't want to convert if different settings for html and latex are wanted or if more control over the latex formatting is needed. But in that case, a new styler instance could easily be used, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right this is by-definition on .to_latex so i think its clear.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Styler now allows styling to be coded in LaTeX or HTML-CSS format. In each separate case, which are completely flexible in each format, you don't want to convert.
But, if you have created an HTML-CSS Styler and it contains the limited attr-values that can now be converted to LaTeX then you should use the convert option. Shouldn't be set to default though.

Convert simple cell-styles from CSS to LaTeX format. Any CSS not found in
conversion table is dropped. A style can be forced by adding option
`--latex`. See notes.

Returns
-------
Expand Down Expand Up @@ -659,6 +664,45 @@ def to_latex(
& ix2 & \$3 & 4.400 & CATS \\
L1 & ix3 & \$2 & 6.600 & COWS \\
\end{tabular}

**CSS Conversion**

This method can convert a Styler constructured with HTML-CSS to LaTeX using
the following limited conversions.

================== ==================== ============= ==========================
CSS Attribute CSS value LaTeX Command LaTeX Options
================== ==================== ============= ==========================
font-weight | bold | bfseries
| bolder | bfseries
font-style | italic | itshape
| oblique | slshape
background-color | red cellcolor | {red}--lwrap
| #fe01ea | [HTML]{FE01EA}--lwrap
| #f0e | [HTML]{FF00EE}--lwrap
| rgb(128,255,0) | [rgb]{0.5,1,0}--lwrap
| rgba(128,0,0,0.5) | [rgb]{0.5,0,0}--lwrap
| rgb(25%,255,50%) | [rgb]{0.25,1,0.5}--lwrap
color | red color | {red}
| #fe01ea | [HTML]{FE01EA}
| #f0e | [HTML]{FF00EE}
| rgb(128,255,0) | [rgb]{0.5,1,0}
| rgba(128,0,0,0.5) | [rgb]{0.5,0,0}
| rgb(25%,255,50%) | [rgb]{0.25,1,0.5}
================== ==================== ============= ==========================

It is also possible to add user-defined LaTeX only styles to a HTML-CSS Styler
using the ``--latex`` flag, and to add LaTeX parsing options that the
converter will detect within a CSS-comment.

>>> df = pd.DataFrame([[1]])
>>> df.style.set_properties(
... **{"font-weight": "bold /* --dwrap */", "Huge": "--latex--rwrap"}
... ).to_latex(css_convert=True)
\begin{tabular}{lr}
{} & {0} \\
0 & {\bfseries}{\Huge{1}} \\
\end{tabular}
"""
table_selectors = (
[style["selector"] for style in self.table_styles]
Expand Down Expand Up @@ -738,6 +782,7 @@ def to_latex(
sparse_columns=sparse_columns,
multirow_align=multirow_align,
multicol_align=multicol_align,
convert_css=convert_css,
)

return save_to_buffer(latex, buf=buf, encoding=encoding)
Expand Down
83 changes: 82 additions & 1 deletion pandas/io/formats/style_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from collections import defaultdict
from functools import partial
import re
from typing import (
Any,
Callable,
Expand Down Expand Up @@ -1115,7 +1116,9 @@ def _parse_latex_table_styles(table_styles: CSSStyles, selector: str) -> str | N
return None


def _parse_latex_cell_styles(latex_styles: CSSList, display_value: str) -> str:
def _parse_latex_cell_styles(
latex_styles: CSSList, display_value: str, convert_css: bool = False
) -> str:
r"""
Mutate the ``display_value`` string including LaTeX commands from ``latex_styles``.

Expand All @@ -1141,6 +1144,8 @@ def _parse_latex_cell_styles(latex_styles: CSSList, display_value: str) -> str:
For example for styles:
`[('c1', 'o1--wrap'), ('c2', 'o2')]` this returns: `{\c1o1 \c2o2{display_value}}
"""
if convert_css:
latex_styles = _parse_latex_css_conversion(latex_styles)
for (command, options) in latex_styles[::-1]: # in reverse for most recent style
formatter = {
"--wrap": f"{{\\{command}--to_parse {display_value}}}",
Expand Down Expand Up @@ -1213,6 +1218,82 @@ def _parse_latex_options_strip(value: str | int | float, arg: str) -> str:
return str(value).replace(arg, "").replace("/*", "").replace("*/", "").strip()


def _parse_latex_css_conversion(styles: CSSList) -> CSSList:
"""
Convert CSS (attribute,value) pairs to equivalent LaTeX (command,options) pairs.

Ignore conversion if tagged with `--latex` option, skipped if no conversion found.
"""

def font_weight(value, arg):
if value == "bold" or value == "bolder":
return "bfseries", f"{arg}"
return None

def font_style(value, arg):
if value == "italic":
return "itshape", f"{arg}"
elif value == "oblique":
return "slshape", f"{arg}"
return None

def color(value, user_arg, command, comm_arg):
"""
CSS colors have 5 formats to process:

- 6 digit hex code: "#ff23ee" --> [HTML]{FF23EE}
- 3 digit hex code: "#f0e" --> [HTML]{FF00EE}
- rgba: rgba(128, 255, 0, 0.5) --> [rgb]{0.502, 1.000, 0.000}
- rgb: rgb(128, 255, 0,) --> [rbg]{0.502, 1.000, 0.000}
- string: red --> {red}

Additionally rgb or rgba can be expressed in % which is also parsed.
"""
arg = user_arg if user_arg != "" else comm_arg

if value[0] == "#" and len(value) == 7: # color is hex code
return command, f"[HTML]{{{value[1:].upper()}}}{arg}"
if value[0] == "#" and len(value) == 4: # color is short hex code
val = f"{value[1].upper()*2}{value[2].upper()*2}{value[3].upper()*2}"
return command, f"[HTML]{{{val}}}{arg}"
elif value[:3] == "rgb": # color is rgb or rgba
r = re.findall("(?<=\\()[0-9\\s%]+(?=,)", value)[0].strip()
r = float(r[:-1]) / 100 if "%" in r else int(r) / 255
g = re.findall("(?<=,)[0-9\\s%]+(?=,)", value)[0].strip()
g = float(g[:-1]) / 100 if "%" in g else int(g) / 255
if value[3] == "a": # color is rgba
b = re.findall("(?<=,)[0-9\\s%]+(?=,)", value)[1].strip()
else: # color is rgb
b = re.findall("(?<=,)[0-9\\s%]+(?=\\))", value)[0].strip()
b = float(b[:-1]) / 100 if "%" in b else int(b) / 255
return command, f"[rgb]{{{r:.3f}, {g:.3f}, {b:.3f}}}{arg}"
else:
return command, f"{{{value}}}{arg}" # color is likely string-named

CONVERTED_ATTRIBUTES: dict[str, Callable] = {
"font-weight": font_weight,
"background-color": partial(color, command="cellcolor", comm_arg="--lwrap"),
"color": partial(color, command="color", comm_arg=""),
"font-style": font_style,
}

latex_styles: CSSList = []
for (attribute, value) in styles:
if isinstance(value, str) and "--latex" in value:
# return the style without conversion but drop '--latex'
latex_styles.append((attribute, value.replace("--latex", "")))
if attribute in CONVERTED_ATTRIBUTES.keys():
arg = ""
for x in ["--wrap", "--nowrap", "--lwrap", "--dwrap", "--rwrap"]:
if x in str(value):
arg, value = x, _parse_latex_options_strip(value, x)
break
latex_style = CONVERTED_ATTRIBUTES[attribute](value, arg)
if latex_style is not None:
latex_styles.extend([latex_style])
return latex_styles


def _escape_latex(s):
r"""
Replace the characters ``&``, ``%``, ``$``, ``#``, ``_``, ``{``, ``}``,
Expand Down
2 changes: 1 addition & 1 deletion pandas/io/formats/templates/latex.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
{% endif %}
{% for row in body %}
{% for c in row %}{% if not loop.first %} & {% endif %}
{%- if c.type == 'th' %}{{parse_header(c, multirow_align, multicol_align)}}{% else %}{{parse_cell(c.cellstyle, c.display_value)}}{% endif %}
{%- if c.type == 'th' %}{{parse_header(c, multirow_align, multicol_align)}}{% else %}{{parse_cell(c.cellstyle, c.display_value, convert_css)}}{% endif %}
{%- endfor %} \\
{% endfor %}
{% set bottomrule = parse_table(table_styles, 'bottomrule') %}
Expand Down
46 changes: 46 additions & 0 deletions pandas/tests/io/formats/style/test_to_latex.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from pandas.io.formats.style import Styler
from pandas.io.formats.style_render import (
_parse_latex_cell_styles,
_parse_latex_css_conversion,
_parse_latex_header_span,
_parse_latex_table_styles,
_parse_latex_table_wrapping,
Expand Down Expand Up @@ -438,3 +439,48 @@ def test_parse_latex_table_wrapping(styler):
overwrite=False,
)
assert _parse_latex_table_wrapping(styler.table_styles, None) is True


@pytest.mark.parametrize(
"css, expected",
[
([("color", "red")], [("color", "{red}")]), # test color and input format types
(
[("color", "rgb(128, 128, 128 )")],
[("color", "[rgb]{0.502, 0.502, 0.502}")],
),
(
[("color", "rgb(128, 50%, 25% )")],
[("color", "[rgb]{0.502, 0.500, 0.250}")],
),
(
[("color", "rgba(128,128,128,1)")],
[("color", "[rgb]{0.502, 0.502, 0.502}")],
),
([("color", "#FF00FF")], [("color", "[HTML]{FF00FF}")]),
([("color", "#F0F")], [("color", "[HTML]{FF00FF}")]),
([("font-weight", "bold")], [("bfseries", "")]), # test font-weight and types
([("font-weight", "bolder")], [("bfseries", "")]),
([("font-weight", "normal")], []),
([("background-color", "red")], [("cellcolor", "{red}--lwrap")]),
(
[("background-color", "#FF00FF")], # test background-color command and wrap
[("cellcolor", "[HTML]{FF00FF}--lwrap")],
),
([("font-style", "italic")], [("itshape", "")]), # test font-style and types
([("font-style", "oblique")], [("slshape", "")]),
([("font-style", "normal")], []),
([("color", "red /*--dwrap*/")], [("color", "{red}--dwrap")]), # css comments
([("background-color", "red /* --dwrap */")], [("cellcolor", "{red}--dwrap")]),
],
)
def test_parse_latex_css_conversion(css, expected):
result = _parse_latex_css_conversion(css)
assert result == expected


def test_parse_latex_css_conversion_option():
css = [("command", "option--latex--wrap")]
expected = [("command", "option--wrap")]
result = _parse_latex_css_conversion(css)
assert result == expected