8000 Add support for capturing svg and png · sphinx-gallery/sphinx-gallery@c827db0 · GitHub
[go: up one dir, main page]

Skip to content

Commit c827db0

Browse files
committed
Add support for capturing svg and png
1 parent 7bd5101 commit c827db0

File tree

10 files changed

+87
-13
lines changed

10 files changed

+87
-13
lines changed

doc/advanced.rst

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,12 @@ Write a custom image scraper
143143
By default, Sphinx-Gallery supports image scraping for Matplotlib
144144
(:func:`~sphinx_gallery.scrapers.matplotlib_scraper`). If you wish to capture
145145
output from other python packages, first determine if the object you wish to
146-
capture has a ``_repr_html_`` method. If so, you can use the configuration
147-
``capture_repr`` (:ref:`capture_repr`) to control the display of the object,
148-
without the need to write a custom scraper. This configuration allows capture
149-
of the raw html output, in a process similar to other html-based displays such
150-
as `jupyter <https://jupyter.org/>`_. If the first option does not work,
146+
capture has any of the other supported capture methods: ``_repr_html_``,
147+
``_repr_png_``, ``_repr_jpeg_``, and ``_repr_svg_``. If so, you can use the
148+
configuration ``capture_repr`` (:ref:`capture_repr`) to control the display of
149+
the object, without the need to write a custom scraper. This configuration allows
150+
capture of the raw html/png/jpeg/svg output, in a process similar to other html-based
151+
displays such as `jupyter <https://jupyter.org/>`_. If the first option does not work,
151152
this section describes how to write a custom scraper.
152153

153154
Image scrapers are functions (or callable class instances) that do the following

doc/configuration.rst

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1856,8 +1856,14 @@ are:
18561856
* ``__str__`` - returns a string containing a nicely printable representation
18571857
of an object. This is what is used when you ``print()`` an object or pass it
18581858
to ``format()``.
1859-
* ``_repr_html_`` - returns a HTML version of the object. This method is only
1860-
present in some objects, for example, pandas dataframes.
1859+
* ``_repr_html_`` - returns an HTML version of the object.
1860+
* ``_repr_png_`` - returns a PNG version of the object.
1861+
* ``_repr_jpeg_`` - returns a JPEG version of the object.
1862+
* ``_repr_svg_`` - returns an SVG version of the object.
1863+
1864+
Note that the last four methods are only available for some objects. For example,
1865+
Pandas dataframes, SymPy expressions, and GraphViz graphs, support one or more of
1866+
these formats.
18611867

18621868
Output capture can be controlled globally by the ``capture_repr`` configuration
18631869
setting or file-by-file by adding a comment to the example file, which overrides

examples/no_output/just_code.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
This demonstrates an example ``.py`` file that is not executed when gallery is
66
generated (see :ref:`build_pattern`) but nevertheless gets included as an
7-
example. Note that no output is capture as this file is not executed.
7+
example. Note that no output is captured as this file is not executed.
88
"""
99

1010
# Code source: Óscar Nájera

examples/plot_3_capture_repr.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@
111111
# default ``capture_repr`` setting, ``_repr_html_`` is attempted to be captured
112112
# first. If this method does not exist, the ``__repr__`` method would be
113113
# captured. If the ``__repr__`` also does not exist (unlikely for non-user
114-
# defined objects), nothing would be captured. For example, if the the
114+
# defined objects), nothing would be captured. For example, if the
115115
# configuration was set to ``'capture_repr': ('_repr_html_')`` nothing would be
116116
# captured for example 2 as ``b`` does not have a ``_repr_html_``.
117117
# You can change the 'representations' in the ``capture_repr`` tuple to finely

sphinx_gallery/directives.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ class ImageSg(images.Image):
128128
/plot_types/basic/images/sphx_glr_bar_001_2_00x.png 2.00x
129129
:class: sphx-glr-single-img
130130
131-
The resulting html is::
131+
The resulting HTML is::
132132
133133
<img src="sphx_glr_bar_001_hidpi.png"
134134
srcset="_images/sphx_glr_bar_001.png,

sphinx_gallery/gen_gallery.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,8 @@ def _fill_gallery_conf_defaults(sphinx_gallery_conf, app=None,
171171

172172
# Check capture_repr
173173
capture_repr = gallery_conf['capture_repr']
174-
supported_reprs = ['__repr__', '__str__', '_repr_html_']
174+
supported_reprs = {'__repr__', '__str__', '_repr_html_', '_repr_png_',
175+
'_repr_svg_', '_repr_jpeg_'}
175176
if isinstance(capture_repr, tuple):
176177
for rep in capture_repr:
177178
if rep not in supported_reprs:
@@ -815,7 +816,7 @@ def _make_graph(fname, entries, gallery_conf):
815816

816817

817818
def write_api_entry_usage(app, docname, source):
818-
"""Write an html page describing which API entries are used and unused.
819+
"""Write an HTML page describing which API entries are used and unused.
819820
820821
To document and graph only those API entries that are used by
821822
autodoc, we have to wait for autodoc to finish and hook into the

sphinx_gallery/gen_rst.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import sys
3131
import traceback
3232
import codeop
33+
import base64
3334

3435
from sphinx.errors import ExtensionError
3536
import sphinx.util
@@ -174,6 +175,21 @@ def __exit__(self, type_, value, tb):
174175
<br />
175176
<br />"""
176177

178+
HTML_BASE64_HEADER = """.. raw:: html
179+
180+
<div class="output_subarea output_html rendered_html output_result">
181+
<img src="data:image/{0};base64,{1}">
182+
</div>
183+
<br />
184+
<br />"""
185+
186+
HTML_SVG_HEADER = """.. raw:: html
187+
188+
<div class="output_subarea output_html rendered_html output_result">
189+
{0}
190+
</div>
191+
<br />
192+
<br />"""
177193

178194
def codestr2rst(codestr, lang='python', lineno=None):
179195
"""Return reStructuredText code block from code string."""
@@ -741,7 +757,8 @@ def _get_last_repr(capture_repr, ___):
741757
last_repr = None
742758
repr_meth = None
743759
else:
744-
if isinstance(last_repr, str):
760+
if (isinstance(last_repr, str) or
761+
isinstance(last_repr, bytes) and meth in ('_repr_png',)):
745762
break
746763
return last_repr, repr_meth
747764

@@ -780,6 +797,14 @@ def _get_code_output(is_last_expr, example_globals, gallery_conf, logging_tee,
780797
# give html output its own header
781798
if repr_meth == '_repr_html_':
782799
captured_html = HTML_HEADER.format(indent(last_repr, ' ' * 4))
800+
elif repr_meth == '_repr_png_':
801+
captured_html = HTML_BASE64_HEADER.format(
802+
'png', base64.b64encode(last_repr).decode())
803+
elif repr_meth == '_repr_jpeg_':
804+
captured_html = HTML_BASE64_HEADER.format(
805+
'jpeg', base64.b64encode(last_repr).decode())
806+
elif repr_meth == '_repr_svg_':
807+
captured_html = HTML_SVG_HEADER.format(indent(last_repr, ' ' * 4))
783808
else:
784809
captured_html = ''
785810

sphinx_gallery/tests/test_gen_rst.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,30 @@ def _repr_html_(self):
830830
831831
"""
832832

833+
code_repr_and_svg = """
834+
class repr_and_svg_class():
835+
def __init__(self):
836+
pass
837+
838+
def __repr__(self):
839+
return "This is the __repr__"
840+
841+
def _repr_svg_(self):
842+
return ("<svg viewBox='0 0 50 50' xmlns='http://www.w3.org/2000/svg'>"
843+
"<line x1='0' y1='0' x2='50' y2='50' stroke='black' />"
844+
"</svg>")
845+
846+
class_inst = repr_and_svg_class()
847+
class_inst
848+
"""
849+
850+
svg_out = """.. raw:: html
851+
852+
<div class="output_subarea output_html rendered_html output_result">
853+
<svg viewBox='0 0 50 50' xmlns='http://www.w3.org/2000/svg'><line x1='0' y1='0' x2='50' y2='50' stroke='black' /></svg>
854+
</div>
855+
<br />
856+
<br />"""
833857

834858
def _clean_output(output):
835859
is_text = '.. rst-class:: sphx-glr-script-out' in output
@@ -879,6 +903,10 @@ def _clean_output(output):
879903
pytest.param(('_repr_html_', '__repr__'), code_repr_only,
880904
'This is the __repr__', id='repr_only,(html,repr)'),
881905
pytest.param(('_repr_html_',), code_plt, '', id='html_none'),
906+
pytest.param(('_repr_svg_',), code_repr_and_svg, svg_out,
907+
id='repr_and_svg,(svg)'),
908+
pytest.param(('__repr__', '_repr_svg_'), code_repr_and_html,
909+
'This is the __repr__', id='repr_and_svg,(repr,svg)'),
882910
])
883911
def test_capture_repr(gallery_conf, capture_repr, code, expected_out,
884912
req_mpl, req_pil, script_vars):

sphinx_gallery/tests/tinybuild/doc/conf.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ def notebook_modification_function(notebook_content, notebook_filename):
125125
'exclude_implicit_doc': ['figure_rst'],
126126
'show_api_usage': True,
127127
'copyfile_regex': r'.*\.rst',
128+
'capture_reps': ('_repr_html_', '_repr_svg_', '__repr__')
128129
}
129130
nitpicky = True
130131
highlight_language = 'python3'

sphinx_gallery/tests/tinybuild/examples/plot_repr.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,15 @@ def _repr_html_(self):
1212

1313

1414
A()
15+
16+
17+
class B:
18+
def _repr_svg_(self):
19+
return """
20+
<svg viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg">
21+
<line x1="0" y1="0" x2="50" y2="50" stroke="black" />
22+
</svg>
23+
"""
24+
25+
26+
B()

0 commit comments

Comments
 (0)
0