diff --git a/CHANGELOG.md b/CHANGELOG.md index 923896f403..56dab1bb95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [4.14.4] - unreleased + +### Added + +- `to_html()` and `write_html()` now allows data compression `compress` to reduce the html file size [3117](https://github.com/plotly/plotly.py/pull/3117) + ## [4.14.3] - 2021-01-12 ### Fixed diff --git a/packages/python/plotly/plotly/basedatatypes.py b/packages/python/plotly/plotly/basedatatypes.py index 7954dea37f..7970af8c8e 100644 --- a/packages/python/plotly/plotly/basedatatypes.py +++ b/packages/python/plotly/plotly/basedatatypes.py @@ -3672,6 +3672,10 @@ def write_html(self, *args, **kwargs): validate: bool (default True) True if the figure should be validated before being converted to JSON, False otherwise. + compress: bool (default False) + If True, the figure data is compressed reducing the total file size. + It adds an external compression library which requires an active + internet connection. auto_open: bool (default True If True, open the saved file in a web browser after saving. This argument only applies if `full_html` is True. diff --git a/packages/python/plotly/plotly/io/_html.py b/packages/python/plotly/plotly/io/_html.py index cb90de32f9..4b073cf995 100644 --- a/packages/python/plotly/plotly/io/_html.py +++ b/packages/python/plotly/plotly/io/_html.py @@ -2,6 +2,8 @@ import json import os import webbrowser +import base64 +import gzip import six @@ -35,6 +37,7 @@ def to_html( default_width="100%", default_height="100%", validate=True, + compress=False, ): """ Convert a figure to an HTML string representation. @@ -121,6 +124,10 @@ def to_html( validate: bool (default True) True if the figure should be validated before being converted to JSON, False otherwise. + compress: bool (default False) + If True, the figure data is compressed reducing the total file size. + It adds an external compression library which requires an active + internet connection. Returns ------- str @@ -230,8 +237,27 @@ def to_html( # Serialize config dict to JSON jconfig = json.dumps(config) + # Compress `jdata` via fflate, and replace `jdata` with a JavaScript variable + script_compress = "" + if compress: + compressed_data = base64.b64encode(gzip.compress(jdata.encode("utf-8"))).decode( + "ascii" + ) + script_compress = """\ + const data_compr_b64 = "{compressed_data}"; + const data_raw = fflate.decompressSync( + fflate.strToU8(atob(data_compr_b64), true) + ); + const data = JSON.parse(fflate.strFromU8(data_raw)); + """.format( + compressed_data=compressed_data + ) + # Replace the plotly data with the variable "data". + jdata = "data" + script = """\ if (document.getElementById("{id}")) {{\ + {script_compress}\ Plotly.newPlot(\ "{id}",\ {data},\ @@ -241,6 +267,7 @@ def to_html( }}""".format( id=plotdivid, data=jdata, + script_compress=script_compress, layout=jlayout, config=jconfig, then_addframes=then_addframes, @@ -300,6 +327,11 @@ def to_html( win_config=_window_plotly_config, plotlyjs=get_plotlyjs() ) + # Add compression library when compression is enabled + load_fflatejs = "" + if compress: + load_fflatejs = '' + # ## Handle loading/initializing MathJax ## include_mathjax_orig = include_mathjax if isinstance(include_mathjax, six.string_types): @@ -343,6 +375,7 @@ def to_html(