Search code examples
node.jsimage-processingmeteorgraphicsmagick

graphicsmagick crop depending on original image size (resuse readstream)


I'm using Meteor's collection-fs packages to upload images, and I want to cut a thumbnail out of the centre of each image using gm(readStrem).crop(). Problem is, the x and y offsets for crop depend on the size of the original image, image sizes will vary, and I can't use the same reasdStream twice.

This breaks:

var xOff = 0;
var yOff = 0;
var thumbnailWidth = 450;
var thumbnailHeight = 600;
gm(readStream).size(function (err, dimensions) {
   if ( dimensions ) {
     xOff = (dimensions.width - thumbnailWidth) / 2;
     yOff = (dimensions.height - thumbnailHeight) / 2;
   }
   gm(readStream)
     .crop(thumbnailWidth, thumbnailHeight, xOff, yOff)
     .stream()
     .pipe(writeStream);
})

The dimensions return but the second use of readStream returns Error: gm().stream() or gm().write() with a non-readable stream

I've seen a few other answers related to this but none help me because the package forces me to pipe(writeStream); I can't just do '.writeAsync()' I tried all sorts of other tricks that didn't work, including:

  • cloning the stream to get the size and then using the original for the synchronous transform and save (in a timer)
  • calling the whole thing synchronously (silly idea, but worth a shot)

If anybody has any ideas, I'd really appreciate your input.

Thanks! db


Solution

  • The solution was to determine the file size on the client, append those details to the fileObj before writing, and then call crop normally on the server during write.

    var cropInfo = fileObj.cropInfo
    gm(readStream)
         .crop(cropInfo.width, cropInfo.height, cropInfo.x, cropInfo.y)
         .stream()
         .pipe(writeStream);
    

    Not quite ideal, but it works.