1
votes

Good evening all.

I'm working with the node gm library with imagemagick for image processing. My intention is to process an image in such a way that it's resized to 2048 height/width (maintaining aspect ratio), and watermarked with a 256x256 image in the bottom right corner.

I'm essentially building an online gallery with social media integration, such that full size images are uploaded and there's one 'optimally sized' image for each relevant social network that has a watermark on it.

I'm having some trouble, here's my current code for sizing to facebook;

gm(origLoc)
    .resize(2048, null)
    .command('composite')
    .gravity('SouthEast')
    .out('-geometry', '+20+10')
    .in(waterMarkFile_Location)
    .write(thisFile.destination + "rsz/FB/"+newFileName, function(err, stdout, stderr, command){
        if (err){
            console.log("Err in FB");
            console.log(err);
        }
    });

It outputs an image that's 2048 pixels wide, maintaining the ratio, but the problem is that the watermark is scaled, covering the whole image.

If I remove or comment the resize line, it watermarks as you would expect but it doesn't resize the entire image to 2048 pixels, maintaining the original image dimensions.

I've been fighting with it for quite a while now, trying numerous solutions, and I seem to be getting this horrible tradeoff. Can anyone help?

1
I think you just need to change the order, put composite after in. Basically, you must load the original and resize, set the gravity and geometry, load the watermark and then composite and save.Mark Setchell
@MarkSetchell Just tried that, resized to 2048 with watermark covering the entire imageScott P
How big is your watermark? Use identify watermark.pngMark Setchell
It's 11KB, 256x256px, PNG format.Scott P
Ok, try this order... load original... resize... load watermark... gravity southeast... geometry +20+10... composite... saveMark Setchell

1 Answers

3
votes

My previous solution relied on the .size function to calculate dimensions for watermark position. I've since adapted the function so it now positions in bottom-right by using gravity;

gm(_image)
    // WATERMARK - PARAM ORDER: [X Pos, Y Pos, width, height]
    .draw(['gravity SouthEast image Over 0,0 256,256 "/path/to/watermark.png"'])
    // RESIZE DIMENSIONS - PARAM ORDER: [width, height]
    .resize(2048, null)
    .write("/path/to/resized/output.jpg", function(err, stdout, stderr, command){
        if (err){
            return cb(err);
        }
        return cb("done");
    });

256 is the desired width and height of my watermark. The comment above the .draw states the order that these values are in - if using gravity to set the position, these are offsets and can be negative.

In this example, the watermark will be in the bottom right corner.

First, you need to .draw the watermark over the image.

Secondly, you need to .resize according to your desired output dimensions.

Finally, you then .write (or .stream) to your output destination.

EDIT - 23:21, Friday 02 December 2016

I've now built a function that lets you resize the among the longest edge, and choose if you want to watermark it - I figure if you're looking for something like this, I greet you "Hello!", people of the future!

function processImgLongEdge(_image, _outputDest, _maxLongEdge, watermark, cb){
    var gm  =   require("gm").subClass({imageMagick: true});
    if (watermark == true){
        gm(_image).size(function(err, value){
            var isLandscape;
            if (value.width > value.height){
                isLandscape =   true;
            } else {
                isLandscape = false;
            }
            if (isLandscape == true){
                gm(_image)
                    .draw(['gravity SouthEast image Over 0,0 256,256 "/full/path/to/watermark.png"'])
                    .resize(_maxLongEdge, null)
                    .write(_outputDest, function(err, stdout, stderr, command){
                        if (err){
                            return cb(err);
                        }
                        return cb("done");
                    });
            } else {
                gm(_image)
                    .draw(['gravity SouthEast image Over 0,0 256,256 "/full/path/to/watermark.png"'])
                    .resize(null, _maxLongEdge)
                    .write(_outputDest, function(err, stdout, stderr, command){
                        if (err){
                            return cb(err);
                        }
                        return cb("done");
                    });
                }
            });
        } else {
            gm(_image).size(function(err, value){
                var isLandscape;
                if (value.width > value.height){
                    isLandscape =   true;
                } else {
                    isLandscape = false;
                }
                if (isLandscape == true){
                    gm(_image)
                        .resize(_maxLongEdge, null)
                        .write(_outputDest, function(err, stdout, stderr, command){
                            if (err){
                                return cb(err);
                            }
                            return cb("done");
                        });
                } else {
                    gm(_image)
                        .resize(null, _maxLongEdge)
                        .write(_outputDest, function(err, stdout, stderr, command){
                            if (err){
                                return cb(err);
                            }
                            return cb("done");
                        });
                }
            });
        }
};

To use it, just go with this and configure as necessary;

processImgLongEdge(
    "/path/to/input/image.jpg",         // Path to original image
    "/path/to/output/image.jpg",        // Path to output image
    600,                                // Max length of longest edge
    false,                              // Should it have a watermark? <true | false>
    function(imgResult){
        console.log(imgResult);         // Will log "done" or error from gm to the console
    }
);

The function could probably be adjusted in some ways, but if you're looking for a 'it just works' solution, this is it.

With some tweaking, you could make it resize along the shortest edge if you prefer, but it's not something I require for my project so I won't cover it here.