"""Figure I/O management utilities.
Provides functions for saving Matplotlib figures in various formats and
rendering them as SVG or other image formats in Jupyter environments.
"""
from __future__ import annotations
__all__ = ["save_formats", "save_and_show", "show"]
from pathlib import Path
from tempfile import NamedTemporaryFile
from xml.dom import minidom
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from ._helpers import create_parent_path
[docs]
def show(image_path: str, size: int = 600, unit: str = "pt") -> None:
"""Load an SVG image and display it at the specified size in a browser or Jupyter.
Parameters
----------
image_path : str
Path to the SVG image to display.
size : int, optional
Desired output width. Default is 600.
unit : str, optional
Unit for the width ('pt', 'px', etc.). Default is 'pt'.
"""
from IPython.display import HTML, SVG, display
svg_obj = SVG(data=image_path)
desired_width = size
# Parse SVG dimensions with defensive handling.
dom = minidom.parseString(svg_obj.data)
doc_el = dom.documentElement
width_attr = doc_el.getAttribute("width") if doc_el else ""
height_attr = doc_el.getAttribute("height") if doc_el else ""
try:
width = float(width_attr.replace(unit, ""))
height = float(height_attr.replace(unit, ""))
except ValueError:
display(HTML(svg_obj.data))
return
if width <= 0:
display(HTML(svg_obj.data))
return
aspect_ratio = height / width
desired_height = int(desired_width * aspect_ratio)
# Replace width attribute.
for w_str in (str(width), str(int(width))):
old = f'width="{w_str}{unit}"'
if old in svg_obj.data:
svg_obj.data = svg_obj.data.replace(
old, f'width="{desired_width}{unit}"'
)
break
# Replace height attribute.
for h_str in (str(height), str(int(height))):
old = f'height="{h_str}{unit}"'
if old in svg_obj.data:
svg_obj.data = svg_obj.data.replace(
old, f'height="{desired_height}{unit}"'
)
break
display(HTML(svg_obj.data))
[docs]
def save_and_show(
fig: Figure,
image_path: str | None = None,
size: int = 600,
unit: str = "pt",
**kwargs,
) -> None:
"""Save a figure to disk, then display it in a Jupyter or web environment.
Parameters
----------
fig : matplotlib.figure.Figure
The Matplotlib figure to save and display.
image_path : str | None, optional
Path to save the image. If None, a system temporary file is used.
size : int, optional
Display width. Default is 600.
unit : str, optional
Unit for the size ('pt', 'px', etc.). Default is 'pt'.
**kwargs
Additional keyword arguments passed to ``savefig``.
"""
if image_path is None:
tmp = NamedTemporaryFile(suffix=".svg", delete=False)
tmp_path = tmp.name
tmp.close()
try:
fig.savefig(tmp_path, bbox_inches=None, **kwargs)
plt.close(fig)
show(tmp_path, size=size, unit=unit)
finally:
Path(tmp_path).unlink(missing_ok=True)
else:
create_parent_path(image_path)
fig.savefig(image_path, bbox_inches=None, **kwargs)
plt.close(fig)
show(image_path, size=size, unit=unit)