Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 00856ad

Browse files
authored
Merge pull request plotly#1979 from plotly/to-image-scale
Scale image in `Plotly.toImage`
2 parents 17237ae + 633882b commit 00856ad

File tree

5 files changed

+90
-11
lines changed

5 files changed

+90
-11
lines changed

src/plot_api/to_image.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@ var attrs = {
4040
'Defaults to the value found in `layout.height`'
4141
].join(' ')
4242
},
43+
scale: {
44+
valType: 'number',
45+
min: 0,
46+
dflt: 1,
47+
description: [
48+
'Sets a scaling for the generated image.',
49+
'If set, all features of a graphs (e.g. text, line width)',
50+
'are scaled, unlike simply setting',
51+
'a bigger *width* and *height*.'
52+
].join(' ')
53+
},
4354
setBackground: {
4455
valType: 'any',
4556
dflt: false,
@@ -111,6 +122,7 @@ function toImage(gd, opts) {
111122
var format = coerce('format');
112123
var width = coerce('width');
113124
var height = coerce('height');
125+
var scale = coerce('scale');
114126
var setBackground = coerce('setBackground');
115127
var imageDataOnly = coerce('imageDataOnly');
116128

@@ -128,7 +140,6 @@ function toImage(gd, opts) {
128140
// extend config for static plot
129141
var configImage = Lib.extendFlat({}, config, {
130142
staticPlot: true,
131-
plotGlPixelRatio: config.plotGlPixelRatio || 2,
132143
setBackground: setBackground
133144
});
134145

@@ -142,7 +153,7 @@ function toImage(gd, opts) {
142153

143154
function convert() {
144155
return new Promise(function(resolve, reject) {
145-
var svg = toSVG(clonedGd);
156+
var svg = toSVG(clonedGd, format, scale);
146157
var width = clonedGd._fullLayout.width;
147158
var height = clonedGd._fullLayout.height;
148159

@@ -164,6 +175,7 @@ function toImage(gd, opts) {
164175
format: format,
165176
width: width,
166177
height: height,
178+
scale: scale,
167179
canvas: canvas,
168180
svg: svg,
169181
// ask svgToImg to return a Promise

src/snapshot/svgtoimg.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ function svgToImg(opts) {
3333
}
3434

3535
var canvas = opts.canvas;
36+
var scale = opts.scale || 1;
37+
var w0 = opts.width || 300;
38+
var h0 = opts.height || 150;
39+
var w1 = scale * w0;
40+
var h1 = scale * h0;
41+
3642
var ctx = canvas.getContext('2d');
3743
var img = new Image();
3844

@@ -41,16 +47,16 @@ function svgToImg(opts) {
4147
// is not restricted to svg
4248
var url = 'data:image/svg+xml,' + encodeURIComponent(svg);
4349

44-
canvas.height = opts.height || 150;
45-
canvas.width = opts.width || 300;
50+
canvas.width = w1;
51+
canvas.height = h1;
4652

4753
img.onload = function() {
4854
var imgData;
4955

5056
// don't need to draw to canvas if svg
5157
// save some time and also avoid failure on IE
5258
if(format !== 'svg') {
53-
ctx.drawImage(img, 0, 0);
59+
ctx.drawImage(img, 0, 0, w1, h1);
5460
}
5561

5662
switch(format) {

src/snapshot/tosvg.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,21 @@ function xmlEntityEncode(str) {
3636
return str.replace(/&(?!\w+;|\#[0-9]+;| \#x[0-9A-F]+;)/g, '&');
3737
}
3838

39-
module.exports = function toSVG(gd, format) {
40-
var fullLayout = gd._fullLayout,
41-
svg = fullLayout._paper,
42-
toppaper = fullLayout._toppaper,
43-
i;
39+
module.exports = function toSVG(gd, format, scale) {
40+
var fullLayout = gd._fullLayout;
41+
var svg = fullLayout._paper;
42+
var toppaper = fullLayout._toppaper;
43+
var width = fullLayout.width;
44+
var height = fullLayout.height;
45+
var i;
4446

4547
// make background color a rect in the svg, then revert after scraping
4648
// all other alterations have been dealt with by properly preparing the svg
4749
// in the first place... like setting cursors with css classes so we don't
4850
// have to remove them, and providing the right namespaces in the svg to
4951
// begin with
5052
svg.insert('rect', ':first-child')
51-
.call(Drawing.setRect, 0, 0, fullLayout.width, fullLayout.height)
53+
.call(Drawing.setRect, 0, 0, width, height)
5254
.call(Color.fill, fullLayout.paper_bgcolor);
5355

5456
// subplot-specific to-SVG methods
@@ -137,6 +139,12 @@ module.exports = function toSVG(gd, format) {
137139
svg.node().setAttributeNS(xmlnsNamespaces.xmlns, 'xmlns', xmlnsNamespaces.svg);
138140
svg.node().setAttributeNS(xmlnsNamespaces.xmlns, 'xmlns:xlink', xmlnsNamespaces.xlink);
139141

142+
if(format === 'svg' && scale) {
143+
svg.attr('width', scale * width);
144+
svg.attr('height', scale * height);
145+
svg.attr('viewBox', '0 0 ' + width + ' ' + height);
146+
}
147+
140148
var s = new window.XMLSerializer().serializeToString(svg.node());
141149
s = htmlEntityDecode(s);
142150
s = xmlEntityEncode(s);

test/jasmine/tests/snapshot_test.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,5 +301,25 @@ describe('Plotly.Snapshot', function() {
301301
.catch(fail)
302302
.then(done);
303303
});
304+
305+
it('should adapt *viewBox* attribute under *scale* option', function(done) {
306+
Plotly.plot(gd, [{
307+
y: [1, 2, 1]
308+
}], {
309+
width: 300,
310+
height: 400
311+
})
312+
.then(function() {
313+
var str = Plotly.Snapshot.toSVG(gd, 'svg', 2.5);
314+
var dom = parser.parseFromString(str, 'image/svg+xml');
315+
var el = dom.getElementsByTagName('svg')[0];
316+
317+
expect(el.getAttribute('width')).toBe('750', 'width');
318+
expect(el.getAttribute('height')).toBe('1000', 'height');
319+
expect(el.getAttribute('viewBox')).toBe('0 0 300 400', 'viewbox');
320+
})
321+
.catch(fail)
322+
.then(done);
323+
});
304324
});
305325
});

test/jasmine/tests/toimage_test.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ var fail = require('../assets/fail_test');
77
var customMatchers = require('../assets/custom_matchers');
88
var subplotMock = require('@mocks/multiple_subplots.json');
99

10+
var FORMATS = ['png', 'jpeg', 'webp', 'svg'];
11+
1012
describe('Plotly.toImage', function() {
1113
'use strict';
1214

@@ -31,6 +33,19 @@ describe('Plotly.toImage', function() {
3133
});
3234
}
3335

36+
function assertSize(url, width, height) {
37+
return new Promise(function(resolve, reject) {
38+
var img = new Image();
39+
img.onload = function() {
40+
expect(img.width).toBe(width, 'image width');
41+
expect(img.height).toBe(height, 'image height');
42+
resolve(url);
43+
};
44+
img.onerror = reject;
45+
img.src = url;
46+
});
47+
}
48+
3449
it('should be attached to Plotly', function() {
3550
expect(Plotly.toImage).toBeDefined();
3651
});
@@ -109,18 +124,22 @@ describe('Plotly.toImage', function() {
109124

110125
Plotly.plot(gd, fig.data, fig.layout)
111126
.then(function() { return Plotly.toImage(gd, {format: 'png'}); })
127+
.then(function(url) { return assertSize(url, 700, 450); })
112128
.then(function(url) {
113129
expect(url.split('png')[0]).toBe('data:image/');
114130
})
115131
.then(function() { return Plotly.toImage(gd, {format: 'jpeg'}); })
132+
.then(function(url) { return assertSize(url, 700, 450); })
116133
.then(function(url) {
117134
expect(url.split('jpeg')[0]).toBe('data:image/');
118135
})
119136
.then(function() { return Plotly.toImage(gd, {format: 'svg'}); })
137+
.then(function(url) { return assertSize(url, 700, 450); })
120138
.then(function(url) {
121139
expect(url.split('svg')[0]).toBe('data:image/');
122140
})
123141
.then(function() { return Plotly.toImage(gd, {format: 'webp'}); })
142+
.then(function(url) { return assertSize(url, 700, 450); })
124143
.then(function(url) {
125144
expect(url.split('webp')[0]).toBe('data:image/');
126145
})
@@ -156,6 +175,20 @@ describe('Plotly.toImage', function() {
156175
.then(done);
157176
});
158177

178+
FORMATS.forEach(function(f) {
179+
it('should respond to *scale* option ( format ' + f + ')', function(done) {
180+
var fig = Lib.extendDeep({}, subplotMock);
181+
182+
Plotly.plot(gd, fig.data, fig.layout)
183+
.then(function() { return Plotly.toImage(gd, {format: f, scale: 2}); })
184+
.then(function(url) { return assertSize(url, 1400, 900); })
185+
.then(function() { return Plotly.toImage(gd, {format: f, scale: 0.5}); })
186+
.then(function(url) { return assertSize(url, 350, 225); })
187+
.catch(fail)
188+
.then(done);
189+
});
190+
});
191+
159192
it('should accept data/layout/config figure object as input', function(done) {
160193
var fig = Lib.extendDeep({}, subplotMock);
161194

0 commit comments

Comments
 (0)