Skip to content

Commit ef74fd3

Browse files
committed
BUG: Base64-encode binary images in MIME bundle
get_mimebundle returned raw bytes for image/png, image/jpeg and application/pdf. The jupyter messaging layer encodes these automatically, but consumers that read the MIME bundle directly do not. In particular, quarto-live's pyodide engine builds a data URL straight from the value, so plots failed to render in a {pyodide} cell. Encode binary image data as base64 strings and decode svg to text so the MIME bundle is valid for direct consumers as well as the jupyter kernel.
1 parent 70630e9 commit ef74fd3

2 files changed

Lines changed: 13 additions & 7 deletions

File tree

plotnine/_utils/ipython.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import base64
34
from typing import TYPE_CHECKING
45

56
if TYPE_CHECKING:
@@ -55,15 +56,22 @@ def get_mimebundle(
5556
}
5657
mimetype = lookup[format]
5758

58-
image: bytes | str = b
5959
metadata: dict[str, DisplayMetadata] = {}
6060
w, h = figure_size_px
6161
if format in ("png", "jpeg"):
6262
metadata = {mimetype: {"width": w, "height": h}}
6363
elif format == "retina":
6464
# `retina=True` in IPython.display.Image just halves width/height
6565
metadata = {mimetype: {"width": w // 2, "height": h // 2}}
66-
elif format == "svg":
67-
image = b.decode()
6866

69-
return {mimetype: image}, metadata
67+
# The display data for a binary image must be base64 encoded and for
68+
# svg it must be text. The jupyter messaging layer encodes the bytes
69+
# automatically, but consumers that read the MIME bundle directly
70+
# (e.g. quarto-live's pyodide engine) do not.
71+
data: str = (
72+
b.decode("utf-8")
73+
if mimetype == "image/svg+xml"
74+
else base64.b64encode(b).decode("ascii")
75+
)
76+
77+
return {mimetype: data}, metadata

plotnine/typing.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,4 @@ class DisplayMetadata(TypedDict):
157157
height: NotRequired[int]
158158

159159

160-
MimeBundle: TypeAlias = tuple[
161-
dict[str, bytes | str], dict[str, DisplayMetadata]
162-
]
160+
MimeBundle: TypeAlias = tuple[dict[str, str], dict[str, DisplayMetadata]]

0 commit comments

Comments
 (0)