@@ -75,7 +75,6 @@ class BaseFigureWidget(BaseFigure, widgets.DOMWidget):
7575 ** custom_serializers )
7676 _py2js_removeTraceProps = Dict (allow_none = True ).tag (sync = True ,
7777 ** custom_serializers )
78- _py2js_svgRequest = Dict (allow_none = True ).tag (sync = True )
7978
8079 # ### JS -> Python message properties ###
8180 # These properties are used to receive messages from the frontend.
@@ -99,8 +98,6 @@ class BaseFigureWidget(BaseFigure, widgets.DOMWidget):
9998 ** custom_serializers )
10099 _js2py_pointsCallback = Dict (allow_none = True ).tag (sync = True ,
101100 ** custom_serializers )
102- _js2py_svgResponse = Dict (allow_none = True ).tag (sync = True ,
103- ** custom_serializers )
104101
105102 # ### Message tracking properties ###
106103 # The _last_layout_edit_id and _last_trace_edit_id properties are used
@@ -168,186 +165,6 @@ def __init__(self,
168165 # views of this widget
169166 self ._view_count = 0
170167
171- # SVG
172- # ---
173- # Dict of pending SVG requests that have been sent to the frontend
174- self ._svg_requests = {}
175-
176- def save_image (self , filename , image_type = None , scale_factor = 2 ):
177- """
178- Save figure to a static image file
179-
180- Parameters
181- ----------
182- filename : str
183- Image output file name
184- image_type : str
185- Image file type. One of: 'svg', 'png', 'pdf', or 'ps'. If not
186- set, file type is inferred from the filename extension
187- scale_factor : number
188- (For png image type) Factor by which to increase the number of
189- pixels in each dimension. A scale factor of 1 will result in a
190- image with pixel dimensions (layout.width, layout.height). A
191- scale factor of 2 will result in an image with dimensions
192- (2*layout.width, 2*layout.height),
193- doubling image's DPI. (Default 2)
194- """
195-
196- # Validate / infer image_type
197- # ---------------------------
198- supported_image_types = ['svg' , 'png' , 'pdf' , 'ps' ]
199- cairo_image_types = ['png' , 'pdf' , 'ps' ]
200- supported_types_csv = ', ' .join (supported_image_types )
201-
202- # ### Image type found ###
203- if not image_type :
204- # Infer image type from extension
205- _ , extension = os .path .splitext (filename )
206-
207- if not extension :
208- raise ValueError ('No image_type specified and file extension has no extension '
209- 'from which to infer an image type '
210- 'Supported image types are: {image_types}'
211- .format (image_types = supported_types_csv ))
212-
213- image_type = extension [1 :]
214-
215- # ### Image type supported ###
216- image_type = image_type .lower ()
217- if image_type not in supported_image_types :
218- raise ValueError ("Unsupported image type '{image_type}'\n "
219- "Supported image types are: {image_types}"
220- .format (image_type = image_type ,
221- image_types = supported_types_csv ))
222-
223- # ### Dependencies available for image type ###
224- # Validate cairo dependency
225- if image_type in cairo_image_types :
226- # Check whether we have cairosvg available
227- try :
228- import_module ('cairosvg' )
229- except ImportError :
230- raise ImportError ('Exporting to {image_type} requires cairosvg'
231- .format (image_type = image_type ))
232-
233- # ### Validate scale_factor ###
234- if not isinstance (scale_factor , numbers .Number ) or scale_factor <= 0 :
235- raise ValueError (
236- 'scale_factor must be a positive number.\n '
237- ' Received: {scale_factor}' .format (
238- scale_factor = scale_factor
239- )
240- )
241-
242- # Build image request
243- # -------------------
244- # ### Create UID for request ###
245- req_id = str (uuid .uuid1 ())
246-
247- # ### Register request ###
248- self ._svg_requests [req_id ] = {'filename' : filename ,
249- 'image_type' : image_type ,
250- 'scale_factor' : scale_factor }
251-
252- # ### Send request to the frontend###
253- self ._py2js_svgRequest = {'request_id' : req_id }
254- self ._py2js_svgRequest = None
255-
256- @observe ('_js2py_svgResponse' )
257- def _handler_js2py_svgResponse (self , change ):
258- """
259- Handle _js2py_svgResponse message from the frontend
260-
261- Parameters
262- ----------
263- change : dict
264- Message dict containing the following keys:
265- - request_id: str
266- The UID of the image request that triggered this message
267- - svg_uri: str
268- The SVG image encoded as a data uri
269- (e.g. 'data:image/svg+xml,...')
270- """
271-
272- # Receive message
273- # ---------------
274- response_data = change ['new' ]
275- self ._js2py_svgResponse = None
276-
277- if not response_data :
278- return
279-
280- # Extract message fields
281- # ----------------------
282- req_id = response_data ['request_id' ]
283- svg_uri = response_data ['svg_uri' ]
284-
285- # Save image
286- # ----------
287- self ._do_save_image (req_id , svg_uri )
288-
289- def _do_save_image (self , req_id , svg_uri ):
290- """
291- Save requested image to a file
292-
293- Parameters
294- ----------
295- req_id : str
296- The UID of the image request that triggered this message
297- svg_uri :
298- The SVG image encoded as a data uri
299- (e.g. 'data:image/svg+xml,...')
300- """
301-
302- # Get request info
303- # ----------------
304- # Lack of request info means that widget has multiple frontend views
305- # and that the request was already processed.
306- req_info = self ._svg_requests .pop (req_id , None )
307- if not req_info :
308- return
309- filename = req_info ['filename' ]
310- image_type = req_info ['image_type' ]
311- scale_factor = req_info ['scale_factor' ]
312-
313- # Convert URI string to svg bytes
314- # -------------------------------
315-
316- # ### Remove URI prefix###
317- if not svg_uri .startswith ('data:image/svg+xml,' ):
318- raise ValueError ('Invalid svg data URI: ' + svg_uri [:20 ])
319-
320- svg = svg_uri .replace ('data:image/svg+xml,' , '' )
321-
322- # ### Unquote special characters ###
323- # (e.g. '%3Csvg%20' -> '<svg ')
324- svg_bytes = parse .unquote (svg ).encode ('utf-8' )
325-
326- # Save as svg
327- # -----------
328- # This requires no external dependencies
329- if image_type == 'svg' :
330- with open (filename , 'wb' ) as f :
331- f .write (svg_bytes )
332-
333- # Save as cairo image type
334- # ------------------------
335- else :
336- # We already made sure cairosvg is available in save_image
337- cairosvg = import_module ('cairosvg' )
338-
339- if image_type == 'png' :
340- cairosvg .svg2png (
341- bytestring = svg_bytes ,
342- write_to = filename ,
343- scale = scale_factor )
344- elif image_type == 'pdf' :
345- cairosvg .svg2pdf (
346- bytestring = svg_bytes , write_to = filename )
347- elif image_type == 'ps' :
348- cairosvg .svg2ps (
349- bytestring = svg_bytes , write_to = filename )
350-
351168 # Python -> JavaScript Messages
352169 # -----------------------------
353170 def _send_relayout_msg (self , layout_data , source_view_id = None ):
0 commit comments