Skip to content

Instantly share code, notes, and snippets.

@chrisjhoughton
Last active January 9, 2021 05:19
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chrisjhoughton/ce73112b59551f8f080d to your computer and use it in GitHub Desktop.
Save chrisjhoughton/ce73112b59551f8f080d to your computer and use it in GitHub Desktop.
Given an image URL or path, crop and resize it to be exactly a specific size. Crops centrally to force enforce the correct aspect ratio, and then resizes as per normal. Depends on the `when` and `gm` NPM modules. Returns a promise that resolves with an image buffer in a .PNG format.
/*
* Given an image URL or path, crop and resize it to be exactly a specific size.
* Crops centrally to force enforce the correct aspect ratio, and then resizes as per normal.
* Depends on the `when` and `gm` NPM modules.
* Returns a promise that resolves with an image buffer in a .PNG format.
*/
var when = require('when');
var gm = require('gm');
var im = gm.subClass({ imageMagick: true }); // use `im` in place of `gm` for heroku compatibility
/*
* Get the size of an image
*/
var getImageSize = function (file) {
return when.promise(function (resolve, reject) {
im(file).size(function (err, size) {
if (err) {
reject(err);
} else {
resolve(size);
}
});
});
};
/*
* Crop and resize an image to precisely the correct dimensions.
* Crops first to get to the correct aspect ratio, and then resizes
* accordingly.
*
* Works with either a URL, or a path.
*/
module.exports = function (url, requiredWidth, requiredHeight) {
return when.promise(function (resolve, reject) {
// We need to resize and then crop. These dimensions
// help us choose how to do this.
var requiredRatio = requiredWidth / requiredHeight;
// Get the size of the image
getImageSize(url)
// Crop to required dimensions and then resize
.then(function (actualSize) {
// If the actual width or height are too small, then reject
if (actualSize.width < requiredWidth || actualSize.height < requiredHeight) {
reject('image_too_small');
return;
}
var actualRatio = actualSize.width / actualSize.height;
// dimensions we'll crop to
var cropWidth;
var cropHeight;
// crop co-ordinates (top left)
var cropX;
var cropY;
// If actual ratio is too high, we need to crop the width
if (actualRatio > requiredRatio) {
cropWidth = actualSize.height * requiredRatio;
cropHeight = actualSize.height; // no change
cropX = (actualSize.width - cropWidth) / 2
cropY = 0;
}
// If actual ratio is too low, we need to crop the height
else if (actualRatio < requiredRatio) {
cropWidth = actualSize.width
cropHeight = actualSize.width / requiredRatio; // no change
cropX = 0;
cropY = (actualSize.height - cropHeight) / 2;
}
// we're already the correct ratio
else {
cropWidth = 0;
cropHeight = 0;
cropX = 0;
cropY = 0;
}
// Crop and resize
im(url)
.crop(cropWidth, cropHeight, cropX, cropY)
.resize(requiredWidth, requiredHeight)
.toBuffer('PNG', function (err, buffer) {
if (err) {
reject(err);
} else {
resolve(buffer);
}
});
});
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment