Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 2k
add new promise-based toImage and downloadImage to Plotly#446
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
642ddba41c31263fca377649e0670f5022e5f8877f05a2448File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| /** | ||
| * Copyright 2012-2016, Plotly, Inc. | ||
| * All rights reserved. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE file in the root directory of this source tree. | ||
| */ | ||
| 'use strict'; | ||
| var Plotly = require('../plotly'); | ||
| var isNumeric = require('fast-isnumeric'); | ||
| /** | ||
| * @param {object} gd figure Object | ||
| * @param {object} opts option object | ||
| * @param opts.format 'jpeg' | 'png' | 'webp' | 'svg' | ||
| * @param opts.width width of snapshot in px | ||
| * @param opts.height height of snapshot in px | ||
| */ | ||
| function toImage(gd, opts) { | ||
| var Snapshot = require('../snapshot'); | ||
| var promise = new Promise(function(resolve, reject) { | ||
| // check for undefined opts | ||
| opts = opts || {}; | ||
| // default to png | ||
| opts.format = opts.format || 'png'; | ||
| var isSizeGood = function(size) { | ||
| ||
| // undefined and null are valid options | ||
| if(size === undefined || size === null) { | ||
| return true; | ||
| } | ||
| if(isNumeric(size) && size > 1) { | ||
| return true; | ||
| } | ||
| return false; | ||
| }; | ||
| if(!isSizeGood(opts.width) || !isSizeGood(opts.height)) { | ||
| reject(new Error('Height and width should be pixel values.')); | ||
| } | ||
| // first clone the GD so we can operate in a clean environment | ||
| var clone = Snapshot.clone(gd, {format: 'png', height: opts.height, width: opts.width}); | ||
| var clonedGd = clone.td; | ||
| // put the cloned div somewhere off screen before attaching to DOM | ||
| clonedGd.style.position = 'absolute'; | ||
| clonedGd.style.left = '-5000px'; | ||
| document.body.appendChild(clonedGd); | ||
| function wait() { | ||
| var delay = Snapshot.getDelay(clonedGd._fullLayout); | ||
| return new Promise(function(resolve, reject) { | ||
| setTimeout(function() { | ||
| var svg = Snapshot.toSVG(clonedGd); | ||
| var canvasContainer = window.document.createElement('div'); | ||
| var canvas = window.document.createElement('canvas'); | ||
| canvasContainer.appendChild(canvas); | ||
| canvasContainer.id = Plotly.Lib.randstr(); | ||
| canvas.id = Plotly.Lib.randstr(); | ||
| Snapshot.svgToImg({ | ||
| format: opts.format, | ||
| width: clonedGd._fullLayout.width, | ||
| height: clonedGd._fullLayout.height, | ||
| canvas: canvas, | ||
| svg: svg, | ||
| // ask svgToImg to return a Promise | ||
| // rather than EventEmitter | ||
| // leave EventEmitter for backward | ||
| // compatibility | ||
| promise: true | ||
| }).then(function(url) { | ||
| if(clonedGd) document.body.removeChild(clonedGd); | ||
| resolve(url); | ||
| }).catch(function(err) { | ||
| reject(err); | ||
| }); | ||
| }, delay); | ||
| }); | ||
| } | ||
| var redrawFunc = Snapshot.getRedrawFunc(clonedGd); | ||
| Plotly.plot(clonedGd, clone.data, clone.layout, clone.config) | ||
| // TODO: the following is Plotly.Plots.redrawText but without the waiting. | ||
| // we shouldn't need to do this, but in *occasional* cases we do. Figure | ||
| // out why and take it out. | ||
| // not sure the above TODO makes sense anymore since | ||
| // we have converted to promises | ||
| .then(redrawFunc) | ||
| .then(wait) | ||
| .then(function(url) { resolve(url); }) | ||
| .catch(function(err) { | ||
| reject(err); | ||
| }); | ||
| }); | ||
| return promise; | ||
| } | ||
| module.exports = toImage; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| /** | ||
| * Copyright 2012-2016, Plotly, Inc. | ||
| * All rights reserved. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE file in the root directory of this source tree. | ||
| */ | ||
| 'use strict'; | ||
| var toImage = require('../plot_api/to_image'); | ||
| var Lib = require('../lib'); // for isIE | ||
| var fileSaver = require('./filesaver'); | ||
| /** | ||
| * @param {object} gd figure Object | ||
| * @param {object} opts option object | ||
| * @param opts.format 'jpeg' | 'png' | 'webp' | 'svg' | ||
| * @param opts.width width of snapshot in px | ||
| * @param opts.height height of snapshot in px | ||
| * @param opts.filename name of file excluding extension | ||
| */ | ||
| function downloadImage(gd, opts) { | ||
| // check for undefined opts | ||
| opts = opts || {}; | ||
| // default to png | ||
| opts.format = opts.format || 'png'; | ||
| return new Promise(function(resolve,reject) { | ||
| if(gd._snapshotInProgress) { | ||
| reject(new Error('Snapshotting already in progress.')); | ||
| } | ||
| // see comments within svgtoimg for additional | ||
| // discussion of problems with IE | ||
| // can now draw to canvas, but CORS tainted canvas | ||
| // does not allow toDataURL | ||
| // svg format will work though | ||
| if(Lib.isIE() && opts.format !== 'svg') { | ||
| reject(new Error('Sorry IE does not support downloading from canvas. Try {format:\'svg\'} instead.')); | ||
| } | ||
| gd._snapshotInProgress = true; | ||
| var promise = toImage(gd, opts); | ||
| var filename = opts.filename || gd.fn || 'newplot'; | ||
| filename += '.' + opts.format; | ||
| promise.then(function(result) { | ||
| gd._snapshotInProgress = false; | ||
| return fileSaver(result,filename); | ||
| }).then(function(name) { | ||
| resolve(name); | ||
| }).catch(function(err) { | ||
| gd._snapshotInProgress = false; | ||
| reject(err); | ||
| }); | ||
| }); | ||
| } | ||
| module.exports = downloadImage; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| /** | ||
| * Copyright 2012-2016, Plotly, Inc. | ||
| * All rights reserved. | ||
| * | ||
| * This source code is licensed under the MIT license found in the | ||
| * LICENSE file in the root directory of this source tree. | ||
| */ | ||
| /* | ||
| * substantial portions of this code from FileSaver.js | ||
| * https://github.com/eligrey/FileSaver.js | ||
| * License: https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md | ||
| * FileSaver.js | ||
| * A saveAs() FileSaver implementation. | ||
| * 1.1.20160328 | ||
| * | ||
| * By Eli Grey, http://eligrey.com | ||
| * License: MIT | ||
| * See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md | ||
| */ | ||
| 'use strict'; | ||
| var fileSaver = function(url, name) { | ||
| var saveLink = document.createElement('a'); | ||
| var canUseSaveLink = 'download' in saveLink; | ||
| var isSafari = /Version\/[\d\.]+.*Safari/.test(navigator.userAgent); | ||
| var promise = new Promise(function(resolve, reject) { | ||
| // IE <10 is explicitly unsupported | ||
| if(typeof navigator !== 'undefined' && /MSIE [1-9]\./.test(navigator.userAgent)) { | ||
| reject(new Error('IE < 10 unsupported')); | ||
| } | ||
| // First try a.download, then web filesystem, then object URLs | ||
| if(isSafari) { | ||
| // Safari doesn't allow downloading of blob urls | ||
| document.location.href = 'data:application/octet-stream' + url.slice(url.search(/[,;]/)); | ||
| resolve(name); | ||
| } | ||
| if(!name) { | ||
| name = 'download'; | ||
| } | ||
| if(canUseSaveLink) { | ||
| saveLink.href = url; | ||
| saveLink.download = name; | ||
| document.body.appendChild(saveLink); | ||
| saveLink.click(); | ||
| document.body.removeChild(saveLink); | ||
| resolve(name); | ||
| } | ||
| // IE 10+ (native saveAs) | ||
| if(typeof navigator !== 'undefined' && navigator.msSaveBlob) { | ||
| navigator.msSaveBlob(new Blob([url]), name); | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Something good in IE?! I'm shocked!
| ||
| resolve(name); | ||
| } | ||
| reject(new Error('download error')); | ||
| }); | ||
| return promise; | ||
| }; | ||
| module.exports = fileSaver; | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@timelyportfolio why did you review this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nevermind. I see. It's here now: https://github.com/plotly/plotly.js/pull/446/files#diff-40d98cbce238527d9bffcf2ecd4efa9bR33