Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 21 additions & 3 deletions plotly/io/_kaleido.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,13 +376,31 @@ def to_image(
if defaults.mathjax:
kopts["mathjax"] = defaults.mathjax

# TODO: Refactor to make it possible to use a shared Kaleido instance here
width = (
width
or fig_dict.get("layout", {}).get("width")
or fig_dict.get("layout", {})
.get("template", {})
.get("layout", {})
.get("width")
or defaults.default_width
)
height = (
height
or fig_dict.get("layout", {}).get("height")
or fig_dict.get("layout", {})
.get("template", {})
.get("layout", {})
.get("height")
or defaults.default_height
)

img_bytes = kaleido.calc_fig_sync(
fig_dict,
opts=dict(
format=format or defaults.default_format,
width=width or defaults.default_width,
height=height or defaults.default_height,
width=width,
height=height,
scale=scale or defaults.default_scale,
),
topojson=defaults.topojson,
Expand Down
60 changes: 60 additions & 0 deletions tests/test_optional/test_kaleido/test_kaleido.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from pathlib import Path
import tempfile
from unittest.mock import patch
import xml.etree.ElementTree as ET

from pdfrw import PdfReader
from PIL import Image
Expand All @@ -16,6 +17,26 @@
fig = {"data": [], "layout": {"title": {"text": "figure title"}}}


def create_figure(width=None, height=None):
"""Create a simple figure with optional layout dimensions."""
layout = {}
if width:
layout["width"] = width
if height:
layout["height"] = height

return go.Figure(data=[go.Scatter(x=[1, 2, 3], y=[1, 2, 3])], layout=layout)


def parse_svg_dimensions(svg_bytes):
"""Parse width and height from SVG bytes."""
svg_str = svg_bytes.decode("utf-8")
root = ET.fromstring(svg_str)
width = root.get("width")
height = root.get("height")
return int(width) if width else None, int(height) if height else None


def check_image(path_or_buffer, size=(700, 500), format="PNG"):
if format == "PDF":
img = PdfReader(path_or_buffer)
Expand Down Expand Up @@ -314,3 +335,42 @@ def test_get_chrome():

# Verify that kaleido.get_chrome_sync was called
mock_get_chrome.assert_called_once()


def test_width_height_priority():
"""Test width/height priority: arguments > layout.width/height > defaults."""

# Test case 1: Arguments override layout
fig = create_figure(width=800, height=600)
svg_bytes = pio.to_image(fig, format="svg", width=1000, height=900)
width, height = parse_svg_dimensions(svg_bytes)
assert width == 1000 and height == 900, (
"Arguments should override layout dimensions"
)

# Test case 2: Layout dimensions used when no arguments
fig = create_figure(width=800, height=600)
svg_bytes = pio.to_image(fig, format="svg")
width, height = parse_svg_dimensions(svg_bytes)
assert width == 800 and height == 600, (
"Layout dimensions should be used when no arguments provided"
)

# Test case 3: Partial override (only width argument)
fig = create_figure(width=800, height=600)
svg_bytes = pio.to_image(fig, format="svg", width=1200)
width, height = parse_svg_dimensions(svg_bytes)
assert width == 1200 and height == 600, (
"Width argument should override layout, height should use layout"
)

# Test case 4: Defaults used when no layout or arguments
fig = create_figure()
svg_bytes = pio.to_image(fig, format="svg")
width, height = parse_svg_dimensions(svg_bytes)
assert width == pio.defaults.default_width, (
"Default width should be used when no layout or argument"
)
assert height == pio.defaults.default_height, (
"Default height should be used when no layout or argument"
)