summaryrefslogtreecommitdiffstats
path: root/sphinx/util/images.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--sphinx/util/images.py111
1 files changed, 111 insertions, 0 deletions
diff --git a/sphinx/util/images.py b/sphinx/util/images.py
new file mode 100644
index 0000000..2def252
--- /dev/null
+++ b/sphinx/util/images.py
@@ -0,0 +1,111 @@
+"""Image utility functions for Sphinx."""
+
+import base64
+import imghdr
+from collections import OrderedDict
+from os import path
+from typing import IO, BinaryIO, NamedTuple, Optional, Tuple
+
+import imagesize
+
+try:
+ from PIL import Image
+except ImportError:
+ Image = None
+
+mime_suffixes = OrderedDict([
+ ('.gif', 'image/gif'),
+ ('.jpg', 'image/jpeg'),
+ ('.png', 'image/png'),
+ ('.pdf', 'application/pdf'),
+ ('.svg', 'image/svg+xml'),
+ ('.svgz', 'image/svg+xml'),
+ ('.ai', 'application/illustrator'),
+])
+
+
+class DataURI(NamedTuple):
+ mimetype: str
+ charset: str
+ data: bytes
+
+
+def get_image_size(filename: str) -> Optional[Tuple[int, int]]:
+ try:
+ size = imagesize.get(filename)
+ if size[0] == -1:
+ size = None
+ elif isinstance(size[0], float) or isinstance(size[1], float):
+ size = (int(size[0]), int(size[1]))
+
+ if size is None and Image: # fallback to Pillow
+ with Image.open(filename) as im:
+ size = im.size
+
+ return size
+ except Exception:
+ return None
+
+
+def guess_mimetype_for_stream(stream: IO, default: Optional[str] = None) -> Optional[str]:
+ imgtype = imghdr.what(stream)
+ if imgtype:
+ return 'image/' + imgtype
+ else:
+ return default
+
+
+def guess_mimetype(filename: str = '', default: Optional[str] = None) -> Optional[str]:
+ _, ext = path.splitext(filename.lower())
+ if ext in mime_suffixes:
+ return mime_suffixes[ext]
+ elif path.exists(filename):
+ with open(filename, 'rb') as f:
+ return guess_mimetype_for_stream(f, default=default)
+
+ return default
+
+
+def get_image_extension(mimetype: str) -> Optional[str]:
+ for ext, _mimetype in mime_suffixes.items():
+ if mimetype == _mimetype:
+ return ext
+
+ return None
+
+
+def parse_data_uri(uri: str) -> Optional[DataURI]:
+ if not uri.startswith('data:'):
+ return None
+
+ # data:[<MIME-type>][;charset=<encoding>][;base64],<data>
+ mimetype = 'text/plain'
+ charset = 'US-ASCII'
+
+ properties, data = uri[5:].split(',', 1)
+ for prop in properties.split(';'):
+ if prop == 'base64':
+ pass # skip
+ elif prop.startswith('charset='):
+ charset = prop[8:]
+ elif prop:
+ mimetype = prop
+
+ image_data = base64.b64decode(data)
+ return DataURI(mimetype, charset, image_data)
+
+
+def test_svg(h: bytes, f: Optional[BinaryIO]) -> Optional[str]:
+ """An additional imghdr library helper; test the header is SVG's or not."""
+ try:
+ if '<svg' in h.decode().lower():
+ return 'svg+xml'
+ except UnicodeDecodeError:
+ pass
+
+ return None
+
+
+# install test_svg() to imghdr
+# refs: https://docs.python.org/3.6/library/imghdr.html#imghdr.tests
+imghdr.tests.append(test_svg)