Skip to content

Commit ffbeb0f

Browse files
committed
Support canvas.toDataURL("image/jpeg") (sync)
Also fixes a bug I introduced in #1152 (JPEG quality needs to go from 0 to 1, not 0 to 100). Fixes #1146
1 parent b10d204 commit ffbeb0f

File tree

3 files changed

+35
-64
lines changed

3 files changed

+35
-64
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ canvas.createJPEGStream() // new
8888
and `canvas.jpegStream()`
8989
* Added `resolution` option for `canvas.toBuffer("image/png")` and
9090
`canvas.createPNGStream()`
91+
* Support for `canvas.toDataURI("image/jpeg")` (sync)
9192

9293
1.6.x (unreleased)
9394
==================

lib/canvas.js

Lines changed: 23 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -99,15 +99,17 @@ Canvas.prototype.createJPEGStream = function(options){
9999
};
100100

101101
/**
102-
* Return a data url. Pass a function for async support (required for "image/jpeg").
102+
* Returns a data URI. Pass a function for async operation (non-standard).
103103
*
104-
* @param {String} type, optional, one of "image/png" or "image/jpeg", defaults to "image/png"
105-
* @param {Object|Number} encoderOptions, optional, options for jpeg compression (see documentation for Canvas#jpegStream) or the JPEG encoding quality from 0 to 1.
106-
* @param {Function} fn, optional, callback for asynchronous operation. Required for type "image/jpeg".
104+
* @param {"image/png"|"image/jpeg"} [type="image/png"] Type.
105+
* @param {Object|Number} [encoderOptions] A number between 0 and 1 indicating
106+
* image quality if the requested type is image/jpeg (standard), or an options
107+
* object for image encoding (see documentation for Canvas#toBuffer)
108+
* (non-standard).
109+
* @param {Function} [fn] Callback for asynchronous operation (non-standard).
107110
* @return {String} data URL if synchronous (callback omitted)
108111
* @api public
109112
*/
110-
111113
Canvas.prototype.toDataURL = function(a1, a2, a3){
112114
// valid arg patterns (args -> [type, opts, fn]):
113115
// [] -> ['image/png', null, null]
@@ -123,6 +125,9 @@ Canvas.prototype.toDataURL = function(a1, a2, a3){
123125
// ['image/jpeg', opts, fn] -> ['image/jpeg', opts, fn]
124126
// ['image/jpeg', qual, fn] -> ['image/jpeg', {quality: qual}, fn]
125127
// ['image/jpeg', undefined, fn] -> ['image/jpeg', null, fn]
128+
// ['image/jpeg'] -> ['image/jpeg', null, fn]
129+
// ['image/jpeg', opts] -> ['image/jpeg', opts, fn]
130+
// ['image/jpeg', qual] -> ['image/jpeg', {quality: qual}, fn]
126131

127132
var type = 'image/png';
128133
var opts = {};
@@ -131,7 +136,7 @@ Canvas.prototype.toDataURL = function(a1, a2, a3){
131136
if ('function' === typeof a1) {
132137
fn = a1;
133138
} else {
134-
if ('string' === typeof a1 && FORMATS.indexOf(a1.toLowerCase()) !== -1) {
139+
if ('string' === typeof a1 && FORMATS.includes(a1.toLowerCase())) {
135140
type = a1.toLowerCase();
136141
}
137142

@@ -141,7 +146,7 @@ Canvas.prototype.toDataURL = function(a1, a2, a3){
141146
if ('object' === typeof a2) {
142147
opts = a2;
143148
} else if ('number' === typeof a2) {
144-
opts = {quality: Math.max(0, Math.min(1, a2)) * 100};
149+
opts = {quality: Math.max(0, Math.min(1, a2))};
145150
}
146151

147152
if ('function' === typeof a3) {
@@ -156,40 +161,19 @@ Canvas.prototype.toDataURL = function(a1, a2, a3){
156161
// Per spec, if the bitmap has no pixels, return this string:
157162
var str = "data:,";
158163
if (fn) {
159-
setTimeout(function() {
160-
fn(null, str);
161-
});
162-
}
163-
return str;
164-
}
165-
166-
if ('image/png' === type) {
167-
if (fn) {
168-
this.toBuffer(function(err, buf){
169-
if (err) return fn(err);
170-
fn(null, 'data:image/png;base64,' + buf.toString('base64'));
171-
});
164+
setTimeout(() => fn(null, str));
165+
return;
172166
} else {
173-
return 'data:image/png;base64,' + this.toBuffer().toString('base64');
174-
}
175-
176-
} else if ('image/jpeg' === type) {
177-
if (undefined === fn) {
178-
throw new Error('Missing required callback function for format "image/jpeg"');
167+
return str;
179168
}
169+
}
180170

181-
var stream = this.jpegStream(opts);
182-
// note that jpegStream is synchronous
183-
var buffers = [];
184-
stream.on('data', function (chunk) {
185-
buffers.push(chunk);
186-
});
187-
stream.on('error', function (err) {
188-
fn(err);
189-
});
190-
stream.on('end', function() {
191-
var result = 'data:image/jpeg;base64,' + Buffer.concat(buffers).toString('base64');
192-
fn(null, result);
193-
});
171+
if (fn) {
172+
this.toBuffer((err, buf) => {
173+
if (err) return fn(err);
174+
fn(null, `data:${type};base64,${buf.toString('base64')}`);
175+
}, type, opts)
176+
} else {
177+
return `data:${type};base64,${this.toBuffer(type).toString('base64')}`
194178
}
195179
};

test/canvas.test.js

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -663,38 +663,31 @@ describe('Canvas', function () {
663663
ctx.fillRect(100,0,100,100);
664664

665665
it('toDataURL() works and defaults to PNG', function () {
666-
assert.ok(0 == canvas.toDataURL().indexOf('data:image/png;base64,'));
666+
assert.ok(canvas.toDataURL().startsWith('data:image/png;base64,'));
667667
});
668668

669669
it('toDataURL(0.5) works and defaults to PNG', function () {
670-
assert.ok(0 == canvas.toDataURL(0.5).indexOf('data:image/png;base64,'));
670+
assert.ok(canvas.toDataURL(0.5).startsWith('data:image/png;base64,'));
671671
});
672672

673673
it('toDataURL(undefined) works and defaults to PNG', function () {
674-
assert.ok(0 == canvas.toDataURL(undefined).indexOf('data:image/png;base64,'));
674+
assert.ok(canvas.toDataURL(undefined).startsWith('data:image/png;base64,'));
675675
});
676676

677677
it('toDataURL("image/png") works', function () {
678-
assert.ok(0 == canvas.toDataURL('image/png').indexOf('data:image/png;base64,'));
678+
assert.ok(canvas.toDataURL('image/png').startsWith('data:image/png;base64,'));
679679
});
680680

681681
it('toDataURL("image/png", 0.5) works', function () {
682-
assert.ok(0 == canvas.toDataURL('image/png').indexOf('data:image/png;base64,'));
682+
assert.ok(canvas.toDataURL('image/png').startsWith('data:image/png;base64,'));
683683
});
684684

685685
it('toDataURL("iMaGe/PNg") works', function () {
686-
assert.ok(0 == canvas.toDataURL('iMaGe/PNg').indexOf('data:image/png;base64,'));
686+
assert.ok(canvas.toDataURL('iMaGe/PNg').startsWith('data:image/png;base64,'));
687687
});
688688

689-
it('toDataURL("image/jpeg") throws', function () {
690-
assert.throws(
691-
function () {
692-
canvas.toDataURL('image/jpeg');
693-
},
694-
function (err) {
695-
return err.message === 'Missing required callback function for format "image/jpeg"';
696-
}
697-
);
689+
it('toDataURL("image/jpeg") works', function () {
690+
assert.ok(canvas.toDataURL('image/jpeg').startsWith('data:image/jpeg;base64,'));
698691
});
699692

700693
it('toDataURL(function (err, str) {...}) works and defaults to PNG', function (done) {
@@ -746,18 +739,11 @@ describe('Canvas', function () {
746739
});
747740

748741
it('toDataURL("image/png", {}) works', function () {
749-
assert.ok(0 == canvas.toDataURL('image/png', {}).indexOf('data:image/png;base64,'));
742+
assert.ok(canvas.toDataURL('image/png', {}).startsWith('data:image/png;base64,'));
750743
});
751744

752-
it('toDataURL("image/jpeg", {}) throws', function () {
753-
assert.throws(
754-
function () {
755-
canvas.toDataURL('image/jpeg', {});
756-
},
757-
function (err) {
758-
return err.message === 'Missing required callback function for format "image/jpeg"';
759-
}
760-
);
745+
it('toDataURL("image/jpeg", {}) works', function () {
746+
assert.ok(canvas.toDataURL('image/jpeg', {}).startsWith('data:image/jpeg;base64,'));
761747
});
762748

763749
it('toDataURL("image/jpeg", function (err, str) {...}) works', function (done) {

0 commit comments

Comments
 (0)