1import base64
2import urllib.parse
3import re
4
5
6STRIP_CHARS = ('\x00\x01\x02\x03\x04\x05\x06\x07\x08\x0b\x0c\x0e\x0f\x10\x11'
7 '\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f')
8
9def bytes_as_data_uri(data, strip_chars=STRIP_CHARS, mime='image/svg+xml'):
10 '''Return a data URI with base64 encoding.'''
11 b64 = base64.b64encode(data)
12 return f'data:{mime};base64,{b64.decode(encoding="ascii")}'
13
14def svg_as_data_uri(txt, strip_chars=STRIP_CHARS, mime='image/svg+xml'):
15 '''Return a data URI with base64 encoding, stripping unsafe chars for SVG.
16 '''
17 search = re.compile('|'.join(strip_chars))
18 data_safe = search.sub(lambda m: '', txt)
19 return bytes_as_data_uri(data_safe.encode(encoding='utf-8'), mime=mime)
20
21def svg_as_utf8_data_uri(txt, unsafe_chars='"', strip_chars=STRIP_CHARS,
22 mime='image/svg+xml'):
23 '''Returns a data URI without base64 encoding.
24
25 The characters '#&%' are always escaped. '#' and '&' break parsing of
26 the data URI. If '%' is not escaped, plain text like '%50' will be
27 incorrectly decoded to 'P'. The characters in `strip_chars` cause the
28 SVG not to render even if they are escaped.
29 '''
30 unsafe_chars = (unsafe_chars or '') + '#&%'
31 replacements = {
32 char: urllib.parse.quote(char, safe='')
33 for char in unsafe_chars
34 }
35 replacements.update({
36 char: ''
37 for char in strip_chars
38 })
39 search = re.compile('|'.join(map(re.escape, replacements.keys())))
40 data_safe = search.sub(lambda m: replacements[m.group(0)], txt)
41 return f'data:{mime};utf8,{data_safe}'