Skip to content

Commit 427a493

Browse files
authored
API: Add Styler.to_html, for saving output to HTML file (#40312)
1 parent 45ad3ef commit 427a493

File tree

7 files changed

+402
-174
lines changed

7 files changed

+402
-174
lines changed

doc/source/whatsnew/v1.3.0.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ properly format HTML and eliminate some inconsistencies (:issue:`39942` :issue:`
142142
One also has greater control of the display through separate sparsification of the index or columns, using the new 'styler' options context (:issue:`41142`).
143143

144144
We have added an extension to allow LaTeX styling as an alternative to CSS styling and a method :meth:`.Styler.to_latex`
145-
which renders the necessary LaTeX format including built-up styles.
145+
which renders the necessary LaTeX format including built-up styles. An additional file io function :meth:`Styler.to_html` has been added for convenience (:issue:`40312`).
146146

147147
Documentation has also seen major revisions in light of new features (:issue:`39720` :issue:`39317` :issue:`40493`)
148148

pandas/io/formats/style.py

+68
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,74 @@ def to_latex(
739739

740740
return save_to_buffer(latex, buf=buf, encoding=encoding)
741741

742+
def to_html(
743+
self,
744+
buf: FilePathOrBuffer[str] | None = None,
745+
*,
746+
table_uuid: str | None = None,
747+
table_attributes: str | None = None,
748+
encoding: str | None = None,
749+
doctype_html: bool = False,
750+
exclude_styles: bool = False,
751+
):
752+
"""
753+
Write Styler to a file, buffer or string in HTML-CSS format.
754+
755+
.. versionadded:: 1.3.0
756+
757+
Parameters
758+
----------
759+
buf : str, Path, or StringIO-like, optional, default None
760+
Buffer to write to. If ``None``, the output is returned as a string.
761+
table_uuid: str, optional
762+
Id attribute assigned to the <table> HTML element in the format:
763+
764+
``<table id="T_<table_uuid>" ..>``
765+
766+
If not given uses Styler's initially assigned value.
767+
table_attributes: str, optional
768+
Attributes to assign within the `<table>` HTML element in the format:
769+
770+
``<table .. <table_attributes> >``
771+
772+
If not given defaults to Styler's preexisting value.
773+
encoding : str, optional
774+
Character encoding setting for file output, and HTML meta tags,
775+
defaults to "utf-8" if None.
776+
doctype_html : bool, default False
777+
Whether to output a fully structured HTML file including all
778+
HTML elements, or just the core ``<style>`` and ``<table>`` elements.
779+
exclude_styles : bool, default False
780+
Whether to include the ``<style>`` element and all associated element
781+
``class`` and ``id`` identifiers, or solely the ``<table>`` element without
782+
styling identifiers.
783+
784+
Returns
785+
-------
786+
str or None
787+
If `buf` is None, returns the result as a string. Otherwise returns `None`.
788+
789+
See Also
790+
--------
791+
DataFrame.to_html: Write a DataFrame to a file, buffer or string in HTML format.
792+
"""
793+
if table_uuid:
794+
self.set_uuid(table_uuid)
795+
796+
if table_attributes:
797+
self.set_table_attributes(table_attributes)
798+
799+
# Build HTML string..
800+
html = self.render(
801+
exclude_styles=exclude_styles,
802+
encoding=encoding if encoding else "utf-8",
803+
doctype_html=doctype_html,
804+
)
805+
806+
return save_to_buffer(
807+
html, buf=buf, encoding=(encoding if buf is not None else None)
808+
)
809+
742810
def set_td_classes(self, classes: DataFrame) -> Styler:
743811
"""
744812
Set the DataFrame of strings added to the ``class`` attribute of ``<td>``

pandas/io/formats/templates/html.tpl

+15-69
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,16 @@
1-
{# Update the template_structure.html document too #}
2-
{%- block before_style -%}{%- endblock before_style -%}
3-
{% block style %}
4-
<style type="text/css">
5-
{% block table_styles %}
6-
{% for s in table_styles %}
7-
#T_{{uuid}} {{s.selector}} {
8-
{% for p,val in s.props %}
9-
{{p}}: {{val}};
10-
{% endfor %}
11-
}
12-
{% endfor %}
13-
{% endblock table_styles %}
14-
{% block before_cellstyle %}{% endblock before_cellstyle %}
15-
{% block cellstyle %}
16-
{% for s in cellstyle %}
17-
{% for selector in s.selectors %}{% if not loop.first %}, {% endif %}#T_{{uuid}}{{selector}}{% endfor %} {
18-
{% for p,val in s.props %}
19-
{{p}}: {{val}};
20-
{% endfor %}
21-
}
22-
{% endfor %}
23-
{% endblock cellstyle %}
24-
</style>
25-
{% endblock style %}
26-
{% block before_table %}{% endblock before_table %}
27-
{% block table %}
28-
<table id="T_{{uuid}}"{% if table_attributes %} {{table_attributes}}{% endif %}>
29-
{% block caption %}
30-
{% if caption %}
31-
<caption>{{caption}}</caption>
1+
{# Update the template_structure.html documentation too #}
2+
{% if doctype_html %}
3+
<!DOCTYPE html>
4+
<html>
5+
<head>
6+
<meta charset="{{encoding}}">
7+
{% if not exclude_styles %}{% include "html_style.tpl" %}{% endif %}
8+
</head>
9+
<body>
10+
{% include "html_table.tpl" %}
11+
</body>
12+
</html>
13+
{% elif not doctype_html %}
14+
{% if not exclude_styles %}{% include "html_style.tpl" %}{% endif %}
15+
{% include "html_table.tpl" %}
3216
{% endif %}
33-
{% endblock caption %}
34-
{% block thead %}
35-
<thead>
36-
{% block before_head_rows %}{% endblock %}
37-
{% for r in head %}
38-
{% block head_tr scoped %}
39-
<tr>
40-
{% for c in r %}
41-
{% if c.is_visible != False %}
42-
<{{c.type}} class="{{c.class}}" {{c.attributes}}>{{c.value}}</{{c.type}}>
43-
{% endif %}
44-
{% endfor %}
45-
</tr>
46-
{% endblock head_tr %}
47-
{% endfor %}
48-
{% block after_head_rows %}{% endblock %}
49-
</thead>
50-
{% endblock thead %}
51-
{% block tbody %}
52-
<tbody>
53-
{% block before_rows %}{% endblock before_rows %}
54-
{% for r in body %}
55-
{% block tr scoped %}
56-
<tr>
57-
{% for c in r %}
58-
{% if c.is_visible != False %}
59-
<{{c.type}} {% if c.id is defined -%} id="T_{{uuid}}{{c.id}}" {%- endif %} class="{{c.class}}" {{c.attributes}}>{{c.display_value}}</{{c.type}}>
60-
{% endif %}
61-
{% endfor %}
62-
</tr>
63-
{% endblock tr %}
64-
{% endfor %}
65-
{% block after_rows %}{% endblock after_rows %}
66-
</tbody>
67-
{% endblock tbody %}
68-
</table>
69-
{% endblock table %}
70-
{% block after_table %}{% endblock after_table %}
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{%- block before_style -%}{%- endblock before_style -%}
2+
{% block style %}
3+
<style type="text/css">
4+
{% block table_styles %}
5+
{% for s in table_styles %}
6+
#T_{{uuid}} {{s.selector}} {
7+
{% for p,val in s.props %}
8+
{{p}}: {{val}};
9+
{% endfor %}
10+
}
11+
{% endfor %}
12+
{% endblock table_styles %}
13+
{% block before_cellstyle %}{% endblock before_cellstyle %}
14+
{% block cellstyle %}
15+
{% for s in cellstyle %}
16+
{% for selector in s.selectors %}{% if not loop.first %}, {% endif %}#T_{{uuid}}{{selector}}{% endfor %} {
17+
{% for p,val in s.props %}
18+
{{p}}: {{val}};
19+
{% endfor %}
20+
}
21+
{% endfor %}
22+
{% endblock cellstyle %}
23+
</style>
24+
{% endblock style %}
+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{% block before_table %}{% endblock before_table %}
2+
{% block table %}
3+
{% if exclude_styles %}
4+
<table>
5+
{% else %}
6+
<table id="T_{{uuid}}"{% if table_attributes %} {{table_attributes}}{% endif %}>
7+
{% endif %}
8+
{% block caption %}
9+
{% if caption %}
10+
<caption>{{caption}}</caption>
11+
{% endif %}
12+
{% endblock caption %}
13+
{% block thead %}
14+
<thead>
15+
{% block before_head_rows %}{% endblock %}
16+
{% for r in head %}
17+
{% block head_tr scoped %}
18+
<tr>
19+
{% if exclude_styles %}
20+
{% for c in r %}
21+
{% if c.is_visible != False %}
22+
<{{c.type}} {{c.attributes}}>{{c.value}}</{{c.type}}>
23+
{% endif %}
24+
{% endfor %}
25+
{% else %}
26+
{% for c in r %}
27+
{% if c.is_visible != False %}
28+
<{{c.type}} class="{{c.class}}" {{c.attributes}}>{{c.value}}</{{c.type}}>
29+
{% endif %}
30+
{% endfor %}
31+
{% endif %}
32+
</tr>
33+
{% endblock head_tr %}
34+
{% endfor %}
35+
{% block after_head_rows %}{% endblock %}
36+
</thead>
37+
{% endblock thead %}
38+
{% block tbody %}
39+
<tbody>
40+
{% block before_rows %}{% endblock before_rows %}
41+
{% for r in body %}
42+
{% block tr scoped %}
43+
<tr>
44+
{% if exclude_styles %}
45+
{% for c in r %}{% if c.is_visible != False %}
46+
<{{c.type}} {{c.attributes}}>{{c.display_value}}</{{c.type}}>
47+
{% endif %}{% endfor %}
48+
{% else %}
49+
{% for c in r %}{% if c.is_visible != False %}
50+
<{{c.type}} {% if c.id is defined -%} id="T_{{uuid}}{{c.id}}" {%- endif %} class="{{c.class}}" {{c.attributes}}>{{c.display_value}}</{{c.type}}>
51+
{% endif %}{% endfor %}
52+
{% endif %}
53+
</tr>
54+
{% endblock tr %}
55+
{% endfor %}
56+
{% block after_rows %}{% endblock after_rows %}
57+
</tbody>
58+
{% endblock tbody %}
59+
</table>
60+
{% endblock table %}
61+
{% block after_table %}{% endblock after_table %}

0 commit comments

Comments
 (0)