diff --git a/docs/api-output.md b/docs/api-output.md
index b5112440..e13cdeab 100644
--- a/docs/api-output.md
+++ b/docs/api-output.md
@@ -40,15 +40,17 @@ Write output to a Buffer.
JPEG, PNG, WebP, and RAW output are supported.
By default, the format will match the input image, except GIF and SVG input which become PNG output.
-`callback`, if present, gets three arguments `(err, buffer, info)` where:
+`callback`, if present, gets three arguments `(err, data, info)` where:
-- `err` is an error message, if any.
-- `buffer` is the output image data.
+- `err` is an error, if any.
+- `data` is the output image data.
- `info` contains the output image `format`, `size` (bytes), `width`, `height` and `channels`.
- A Promises/A+ promise is returned when `callback` is not provided.
+ A Promise is returned when `callback` is not provided.
**Parameters**
+- `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
+ - `options.resolveWithObject` **[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)?** Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
- `callback` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)?**
Returns **[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Buffer](https://nodejs.org/api/buffer.html)>** when no callback is provided
diff --git a/docs/changelog.md b/docs/changelog.md
index a2afd224..3a8f2733 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -4,6 +4,12 @@
Requires libvips v8.4.2.
+#### v0.17.3 - TBD
+
+* Allow toBuffer to optionally resolve a Promise with both info and data.
+ [#143](https://github.com/lovell/sharp/issues/143)
+ [@salzhrani](https://github.com/salzhrani)
+
#### v0.17.2 - 11th February 2017
* Ensure Readable side of Stream can start flowing after Writable side has finished.
diff --git a/lib/constructor.js b/lib/constructor.js
index 920b5436..dc490751 100644
--- a/lib/constructor.js
+++ b/lib/constructor.js
@@ -134,6 +134,7 @@ const Sharp = function (input, options) {
streamOut: false,
withMetadata: false,
withMetadataOrientation: -1,
+ resolveWithObject: false,
// output format
jpegQuality: 80,
jpegProgressive: false,
diff --git a/lib/output.js b/lib/output.js
index bc65d77f..75ae221a 100644
--- a/lib/output.js
+++ b/lib/output.js
@@ -48,17 +48,24 @@ const toFile = function toFile (fileOut, callback) {
* JPEG, PNG, WebP, and RAW output are supported.
* By default, the format will match the input image, except GIF and SVG input which become PNG output.
*
- * `callback`, if present, gets three arguments `(err, buffer, info)` where:
- * - `err` is an error message, if any.
- * - `buffer` is the output image data.
+ * `callback`, if present, gets three arguments `(err, data, info)` where:
+ * - `err` is an error, if any.
+ * - `data` is the output image data.
* - `info` contains the output image `format`, `size` (bytes), `width`, `height` and `channels`.
- * A Promises/A+ promise is returned when `callback` is not provided.
+ * A Promise is returned when `callback` is not provided.
*
+ * @param {Object} [options]
+ * @param {Boolean} [options.resolveWithObject] Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
* @param {Function} [callback]
* @returns {Promise} - when no callback is provided
*/
-const toBuffer = function toBuffer (callback) {
- return this._pipeline(callback);
+const toBuffer = function toBuffer (options, callback) {
+ if (is.object(options)) {
+ if (is.bool(options.resolveWithObject)) {
+ this.options.resolveWithObject = options.resolveWithObject;
+ }
+ }
+ return this._pipeline(is.fn(options) ? options : callback);
};
/**
@@ -423,11 +430,15 @@ const _pipeline = function _pipeline (callback) {
return new Promise(function (resolve, reject) {
that.on('finish', function () {
that._flattenBufferIn();
- sharp.pipeline(that.options, function (err, data) {
+ sharp.pipeline(that.options, function (err, data, info) {
if (err) {
reject(err);
} else {
- resolve(data);
+ if (that.options.resolveWithObject) {
+ resolve({ data: data, info: info });
+ } else {
+ resolve(data);
+ }
}
});
});
@@ -435,11 +446,15 @@ const _pipeline = function _pipeline (callback) {
} else {
// output=promise, input=file/buffer
return new Promise(function (resolve, reject) {
- sharp.pipeline(that.options, function (err, data) {
+ sharp.pipeline(that.options, function (err, data, info) {
if (err) {
reject(err);
} else {
- resolve(data);
+ if (that.options.resolveWithObject) {
+ resolve({ data: data, info: info });
+ } else {
+ resolve(data);
+ }
}
});
});
diff --git a/test/unit/io.js b/test/unit/io.js
index 7806c32c..894848a2 100644
--- a/test/unit/io.js
+++ b/test/unit/io.js
@@ -77,16 +77,58 @@ describe('Input/output', function () {
readable.pipe(pipeline);
});
- it('Read from Stream and write to Buffer via Promise', function (done) {
- const readable = fs.createReadStream(fixtures.inputJpg);
+ it('Read from Stream and write to Buffer via Promise resolved with Buffer', function () {
const pipeline = sharp().resize(1, 1);
- pipeline.toBuffer().then(function (data) {
- assert.strictEqual(true, data.length > 0);
- done();
- }).catch(function (err) {
- throw err;
- });
- readable.pipe(pipeline);
+ fs.createReadStream(fixtures.inputJpg).pipe(pipeline);
+ return pipeline
+ .toBuffer({resolveWithObject: false})
+ .then(function (data) {
+ assert.strictEqual(true, data instanceof Buffer);
+ assert.strictEqual(true, data.length > 0);
+ });
+ });
+
+ it('Read from Stream and write to Buffer via Promise resolved with Object', function () {
+ const pipeline = sharp().resize(1, 1);
+ fs.createReadStream(fixtures.inputJpg).pipe(pipeline);
+ return pipeline
+ .toBuffer({resolveWithObject: true})
+ .then(function (object) {
+ assert.strictEqual('object', typeof object);
+ assert.strictEqual('object', typeof object.info);
+ assert.strictEqual('jpeg', object.info.format);
+ assert.strictEqual(1, object.info.width);
+ assert.strictEqual(1, object.info.height);
+ assert.strictEqual(3, object.info.channels);
+ assert.strictEqual(true, object.data instanceof Buffer);
+ assert.strictEqual(true, object.data.length > 0);
+ });
+ });
+
+ it('Read from File and write to Buffer via Promise resolved with Buffer', function () {
+ return sharp(fixtures.inputJpg)
+ .resize(1, 1)
+ .toBuffer({resolveWithObject: false})
+ .then(function (data) {
+ assert.strictEqual(true, data instanceof Buffer);
+ assert.strictEqual(true, data.length > 0);
+ });
+ });
+
+ it('Read from File and write to Buffer via Promise resolved with Object', function () {
+ return sharp(fixtures.inputJpg)
+ .resize(1, 1)
+ .toBuffer({resolveWithObject: true})
+ .then(function (object) {
+ assert.strictEqual('object', typeof object);
+ assert.strictEqual('object', typeof object.info);
+ assert.strictEqual('jpeg', object.info.format);
+ assert.strictEqual(1, object.info.width);
+ assert.strictEqual(1, object.info.height);
+ assert.strictEqual(3, object.info.channels);
+ assert.strictEqual(true, object.data instanceof Buffer);
+ assert.strictEqual(true, object.data.length > 0);
+ });
});
it('Read from Stream and write to Stream', function (done) {