Skip to content

Commit 9ca942b

Browse files
Support images with non-base64 data URIs (#14006)
Co-authored-by: Adam Turner <9087854+aa-turner@users.noreply.github.com>
1 parent 707f210 commit 9ca942b

File tree

3 files changed

+21
-4
lines changed

3 files changed

+21
-4
lines changed

CHANGES.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@ Bugs fixed
154154
Patch by Harmen Stoppels.
155155
* #13944: autodoc: show traceback during import in human readable representation.
156156
Patch by Florian Best.
157+
* #14006: Support images with data URIs that aren't base64-encoded.
158+
Patch by Shengyu Zhang and Adam Turner.
157159

158160

159161
Testing

sphinx/util/images.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import base64
66
from pathlib import Path
77
from typing import TYPE_CHECKING, NamedTuple, overload
8+
from urllib.parse import unquote_to_bytes
89

910
import imagesize
1011

@@ -90,7 +91,6 @@ def get_image_extension(mimetype: str) -> str | None:
9091
def parse_data_uri(uri: str) -> DataURI | None:
9192
if not uri.startswith('data:'):
9293
return None
93-
uri = uri[5:]
9494

9595
if ',' not in uri:
9696
msg = 'malformed data URI'
@@ -100,16 +100,19 @@ def parse_data_uri(uri: str) -> DataURI | None:
100100
mimetype = 'text/plain'
101101
charset = 'US-ASCII'
102102

103+
uri = uri[5:]
103104
properties, _, data = uri.partition(',')
104105
for prop in properties.split(';'):
105106
if prop == 'base64':
106107
pass # skip
107-
elif prop.startswith('charset='):
108+
elif prop.lower().startswith('charset='):
108109
charset = prop[8:]
109110
elif prop:
110-
mimetype = prop
111+
mimetype = prop.lower()
111112

112-
image_data = base64.b64decode(data)
113+
image_data = unquote_to_bytes(data) # data might be percent-encoded
114+
if properties.endswith(';base64'):
115+
image_data = base64.decodebytes(image_data)
113116
return DataURI(mimetype, charset, image_data)
114117

115118

tests/test_util/test_util_images.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,15 @@ def test_parse_data_uri() -> None:
8686
)
8787
with pytest.raises(ValueError, match=r'malformed data URI'):
8888
parse_data_uri(uri)
89+
90+
# not base64
91+
uri = (
92+
'data:image/svg+xml,%3Csvg%20width%3D%22100%22%20height%3D%22100%22%20'
93+
'xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Ccircle%20cx'
94+
'%3D%2250%22%20cy%3D%2250%22%20r%3D%2240%22%20fill%3D%22blue%22%2F%3E'
95+
'%3C%2Fsvg%3E'
96+
)
97+
image = parse_data_uri(uri)
98+
assert image is not None
99+
assert image.mimetype == 'image/svg+xml'
100+
assert b'%' not in image.data

0 commit comments

Comments
 (0)