##// END OF EJS Templates
Merge pull request #10748 from minrk/gif-without-magic...
Thomas Kluyver -
r23853:ea43274b merge
parent child Browse files
Show More
@@ -81,17 +81,7 b' def publish_display_data(data, metadata=None, source=None, *, transient=None, **'
81 See the ``display_data`` message in the messaging documentation for
81 See the ``display_data`` message in the messaging documentation for
82 more details about this message type.
82 more details about this message type.
83
83
84 The following MIME types are currently implemented:
84 Keys of data and metadata can be any mime-type.
85
86 * text/plain
87 * text/html
88 * text/markdown
89 * text/latex
90 * application/json
91 * application/javascript
92 * image/png
93 * image/jpeg
94 * image/svg+xml
95
85
96 Parameters
86 Parameters
97 ----------
87 ----------
@@ -254,7 +244,8 b' def display(*objs, include=None, exclude=None, metadata=None, transient=None, di'
254 - `_repr_svg_`: return raw SVG data as a string
244 - `_repr_svg_`: return raw SVG data as a string
255 - `_repr_latex_`: return LaTeX commands in a string surrounded by "$".
245 - `_repr_latex_`: return LaTeX commands in a string surrounded by "$".
256 - `_repr_mimebundle_`: return a full mimebundle containing the mapping
246 - `_repr_mimebundle_`: return a full mimebundle containing the mapping
257 from all mimetypes to data
247 from all mimetypes to data.
248 Use this for any mime-type not listed above.
258
249
259 When you are directly writing your own classes, you can adapt them for
250 When you are directly writing your own classes, you can adapt them for
260 display in IPython by following the above approach. But in practice, you
251 display in IPython by following the above approach. But in practice, you
@@ -942,8 +933,7 b' def _pngxy(data):'
942 """read the (width, height) from a PNG header"""
933 """read the (width, height) from a PNG header"""
943 ihdr = data.index(b'IHDR')
934 ihdr = data.index(b'IHDR')
944 # next 8 bytes are width/height
935 # next 8 bytes are width/height
945 w4h4 = data[ihdr+4:ihdr+12]
936 return struct.unpack('>ii', data[ihdr+4:ihdr+12])
946 return struct.unpack('>ii', w4h4)
947
937
948 def _jpegxy(data):
938 def _jpegxy(data):
949 """read the (width, height) from a JPEG header"""
939 """read the (width, height) from a JPEG header"""
@@ -964,17 +954,28 b' def _jpegxy(data):'
964 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
954 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
965 return w, h
955 return w, h
966
956
957 def _gifxy(data):
958 """read the (width, height) from a GIF header"""
959 return struct.unpack('<HH', data[6:10])
960
961
967 class Image(DisplayObject):
962 class Image(DisplayObject):
968
963
969 _read_flags = 'rb'
964 _read_flags = 'rb'
970 _FMT_JPEG = u'jpeg'
965 _FMT_JPEG = u'jpeg'
971 _FMT_PNG = u'png'
966 _FMT_PNG = u'png'
972 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG]
967 _FMT_GIF = u'gif'
968 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF]
969 _MIMETYPES = {
970 _FMT_PNG: 'image/png',
971 _FMT_JPEG: 'image/jpeg',
972 _FMT_GIF: 'image/gif',
973 }
973
974
974 def __init__(self, data=None, url=None, filename=None, format=None,
975 def __init__(self, data=None, url=None, filename=None, format=None,
975 embed=None, width=None, height=None, retina=False,
976 embed=None, width=None, height=None, retina=False,
976 unconfined=False, metadata=None):
977 unconfined=False, metadata=None):
977 """Create a PNG/JPEG image object given raw data.
978 """Create a PNG/JPEG/GIF image object given raw data.
978
979
979 When this object is returned by an input cell or passed to the
980 When this object is returned by an input cell or passed to the
980 display function, it will result in the image being displayed
981 display function, it will result in the image being displayed
@@ -992,7 +993,7 b' class Image(DisplayObject):'
992 Path to a local file to load the data from.
993 Path to a local file to load the data from.
993 Images from a file are always embedded.
994 Images from a file are always embedded.
994 format : unicode
995 format : unicode
995 The format of the image data (png/jpeg/jpg). If a filename or URL is given
996 The format of the image data (png/jpeg/jpg/gif). If a filename or URL is given
996 for format will be inferred from the filename extension.
997 for format will be inferred from the filename extension.
997 embed : bool
998 embed : bool
998 Should the image data be embedded using a data URI (True) or be
999 Should the image data be embedded using a data URI (True) or be
@@ -1054,6 +1055,8 b' class Image(DisplayObject):'
1054 format = self._FMT_JPEG
1055 format = self._FMT_JPEG
1055 if ext == u'png':
1056 if ext == u'png':
1056 format = self._FMT_PNG
1057 format = self._FMT_PNG
1058 if ext == u'gif':
1059 format = self._FMT_GIF
1057 else:
1060 else:
1058 format = ext.lower()
1061 format = ext.lower()
1059 elif isinstance(data, bytes):
1062 elif isinstance(data, bytes):
@@ -1064,7 +1067,7 b' class Image(DisplayObject):'
1064
1067
1065 # failed to detect format, default png
1068 # failed to detect format, default png
1066 if format is None:
1069 if format is None:
1067 format = 'png'
1070 format = self._FMT_PNG
1068
1071
1069 if format.lower() == 'jpg':
1072 if format.lower() == 'jpg':
1070 # jpg->jpeg
1073 # jpg->jpeg
@@ -1075,6 +1078,9 b' class Image(DisplayObject):'
1075
1078
1076 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
1079 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
1077 raise ValueError("Cannot embed the '%s' image format" % (self.format))
1080 raise ValueError("Cannot embed the '%s' image format" % (self.format))
1081 if self.embed:
1082 self._mimetype = self._MIMETYPES.get(self.format)
1083
1078 self.width = width
1084 self.width = width
1079 self.height = height
1085 self.height = height
1080 self.retina = retina
1086 self.retina = retina
@@ -1091,14 +1097,17 b' class Image(DisplayObject):'
1091 if retina:
1097 if retina:
1092 self._retina_shape()
1098 self._retina_shape()
1093
1099
1100
1094 def _retina_shape(self):
1101 def _retina_shape(self):
1095 """load pixel-doubled width and height from image data"""
1102 """load pixel-doubled width and height from image data"""
1096 if not self.embed:
1103 if not self.embed:
1097 return
1104 return
1098 if self.format == 'png':
1105 if self.format == self._FMT_PNG:
1099 w, h = _pngxy(self.data)
1106 w, h = _pngxy(self.data)
1100 elif self.format == 'jpeg':
1107 elif self.format == self._FMT_JPEG:
1101 w, h = _jpegxy(self.data)
1108 w, h = _jpegxy(self.data)
1109 elif self.format == self._FMT_GIF:
1110 w, h = _gifxy(self.data)
1102 else:
1111 else:
1103 # retina only supports png
1112 # retina only supports png
1104 return
1113 return
@@ -1128,7 +1137,21 b' class Image(DisplayObject):'
1128 klass=klass,
1137 klass=klass,
1129 )
1138 )
1130
1139
1131 def _data_and_metadata(self):
1140 def _repr_mimebundle_(self, include=None, exclude=None):
1141 """Return the image as a mimebundle
1142
1143 Any new mimetype support should be implemented here.
1144 """
1145 if self.embed:
1146 mimetype = self._mimetype
1147 data, metadata = self._data_and_metadata(always_both=True)
1148 if metadata:
1149 metadata = {mimetype: metadata}
1150 return {mimetype: data}, metadata
1151 else:
1152 return {'text/html': self._repr_html_()}
1153
1154 def _data_and_metadata(self, always_both=False):
1132 """shortcut for returning metadata with shape information, if defined"""
1155 """shortcut for returning metadata with shape information, if defined"""
1133 b64_data = b2a_base64(self.data).decode('ascii')
1156 b64_data = b2a_base64(self.data).decode('ascii')
1134 md = {}
1157 md = {}
@@ -1140,22 +1163,23 b' class Image(DisplayObject):'
1140 md['height'] = self.height
1163 md['height'] = self.height
1141 if self.unconfined:
1164 if self.unconfined:
1142 md['unconfined'] = self.unconfined
1165 md['unconfined'] = self.unconfined
1143 if md:
1166 if md or always_both:
1144 return b64_data, md
1167 return b64_data, md
1145 else:
1168 else:
1146 return b64_data
1169 return b64_data
1147
1170
1148 def _repr_png_(self):
1171 def _repr_png_(self):
1149 if self.embed and self.format == u'png':
1172 if self.embed and self.format == self._FMT_PNG:
1150 return self._data_and_metadata()
1173 return self._data_and_metadata()
1151
1174
1152 def _repr_jpeg_(self):
1175 def _repr_jpeg_(self):
1153 if self.embed and (self.format == u'jpeg' or self.format == u'jpg'):
1176 if self.embed and self.format == self._FMT_JPEG:
1154 return self._data_and_metadata()
1177 return self._data_and_metadata()
1155
1178
1156 def _find_ext(self, s):
1179 def _find_ext(self, s):
1157 return s.split('.')[-1].lower()
1180 return s.split('.')[-1].lower()
1158
1181
1182
1159 class Video(DisplayObject):
1183 class Video(DisplayObject):
1160
1184
1161 def __init__(self, data=None, url=None, filename=None, embed=False, mimetype=None):
1185 def __init__(self, data=None, url=None, filename=None, embed=False, mimetype=None):
@@ -1254,12 +1278,6 b' class Video(DisplayObject):'
1254 # TODO
1278 # TODO
1255 pass
1279 pass
1256
1280
1257 def _repr_png_(self):
1258 # TODO
1259 pass
1260 def _repr_jpeg_(self):
1261 # TODO
1262 pass
1263
1281
1264 def clear_output(wait=False):
1282 def clear_output(wait=False):
1265 """Clear the output of the current cell receiving output.
1283 """Clear the output of the current cell receiving output.
@@ -967,10 +967,7 b' class MimeBundleFormatter(BaseFormatter):'
967 method = get_real_method(obj, self.print_method)
967 method = get_real_method(obj, self.print_method)
968
968
969 if method is not None:
969 if method is not None:
970 d = {}
970 return method(include=include, exclude=exclude)
971 d['include'] = include
972 d['exclude'] = exclude
973 return method(**d)
974 return None
971 return None
975 else:
972 else:
976 return None
973 return None
@@ -996,19 +993,6 b' def format_display_data(obj, include=None, exclude=None):'
996
993
997 By default all format types will be computed.
994 By default all format types will be computed.
998
995
999 The following MIME types are currently implemented:
1000
1001 * text/plain
1002 * text/html
1003 * text/markdown
1004 * text/latex
1005 * application/json
1006 * application/javascript
1007 * application/pdf
1008 * image/png
1009 * image/jpeg
1010 * image/svg+xml
1011
1012 Parameters
996 Parameters
1013 ----------
997 ----------
1014 obj : object
998 obj : object
@@ -32,6 +32,15 b' def test_image_size():'
32 nt.assert_equal(u'<img src="%s" class="unconfined"/>' % (thisurl), img._repr_html_())
32 nt.assert_equal(u'<img src="%s" class="unconfined"/>' % (thisurl), img._repr_html_())
33
33
34
34
35 def test_image_mimes():
36 fmt = get_ipython().display_formatter.format
37 for format in display.Image._ACCEPTABLE_EMBEDDINGS:
38 mime = display.Image._MIMETYPES[format]
39 img = display.Image(b'garbage', format=format)
40 data, metadata = fmt(img)
41 nt.assert_equal(sorted(data), sorted([mime, 'text/plain']))
42
43
35 def test_geojson():
44 def test_geojson():
36
45
37 gj = display.GeoJSON(data={
46 gj = display.GeoJSON(data={
@@ -77,7 +86,7 b' def test_base64image():'
77 def test_image_filename_defaults():
86 def test_image_filename_defaults():
78 '''test format constraint, and validity of jpeg and png'''
87 '''test format constraint, and validity of jpeg and png'''
79 tpath = ipath.get_ipython_package_dir()
88 tpath = ipath.get_ipython_package_dir()
80 nt.assert_raises(ValueError, display.Image, filename=os.path.join(tpath, 'testing/tests/badformat.gif'),
89 nt.assert_raises(ValueError, display.Image, filename=os.path.join(tpath, 'testing/tests/badformat.zip'),
81 embed=True)
90 embed=True)
82 nt.assert_raises(ValueError, display.Image)
91 nt.assert_raises(ValueError, display.Image)
83 nt.assert_raises(ValueError, display.Image, data='this is not an image', format='badformat', embed=True)
92 nt.assert_raises(ValueError, display.Image, data='this is not an image', format='badformat', embed=True)
@@ -361,3 +370,4 b' def test_display_handle():'
361 },
370 },
362 'update': True,
371 'update': True,
363 })
372 })
373
@@ -30,7 +30,10 b' class RichOutput(object):'
30 return data, self.metadata[mime]
30 return data, self.metadata[mime]
31 else:
31 else:
32 return data
32 return data
33
33
34 def _repr_mimebundle_(self, include=None, exclude=None):
35 return self.data, self.metadata
36
34 def _repr_html_(self):
37 def _repr_html_(self):
35 return self._repr_mime_("text/html")
38 return self._repr_mime_("text/html")
36
39
@@ -162,5 +165,3 b' class capture_output(object):'
162 if self.display and self.shell:
165 if self.display and self.shell:
163 self.shell.display_pub = self.save_display_pub
166 self.shell.display_pub = self.save_display_pub
164 sys.displayhook = self.save_display_hook
167 sys.displayhook = self.save_display_hook
165
166
General Comments 0
You need to be logged in to leave comments. Login now