how to pass image buffer data to gm in() GraphicsMagic
Asked Answered
A

4

31
var buf = require('fs').readFileSync('test.jpg');

gm().in('-page', '+0+0').in(buf,'test.jpg').write('output.jpg', function (err) {
     if (err) console.log(err);
})

in this case i want to pass buffer data as input to the gm.in() method.

Below is the link I'm referencing but in it, an image path is used as an input. I want to use buffer data as an input. How can I do this?

Tile four images together using Node.js and GraphicsMagick

Antipersonnel answered 30/6, 2014 at 12:18 Comment(2)
Trying to do the same thing, I want to use composite on aws lambda so I have to work solely with streams. I was thinking that you could name streams and reuse them but no success so far.Sternutatory
There is a page function on gm also -- so instead of using in("-page"), pass the buffer to page function directly. That, I think should work.Allmon
F
4

Without modifying the source of GraphicsMagick itself, you can't. The gm module interacts with the GraphicsMagick program through the command line. The arguments you're passing through the .in() method are being converted into command-line arguments. The GraphicsMagick program only accepts filenames for this argument and will not attempt to process any direct form of data.

If you really needed to make this work without the filesystem, you could always download the GraphicsMagick source code and change the CLI to accept some form of data blob instead of a URL for this argument.

Firstfoot answered 16/8, 2015 at 3:34 Comment(0)
A
2

Actually I was creating a poster with two different image one is template image "background" and 2nd is top image with some text. I tried with gm but i loose image quality. Someone guide me to use Buffer data as input to improve the image quality. I tried but don't know how to pass Buffer data as an input. So finally i decided to use node child process with command string. Here is the sample code i am sharing with you.

var fs = require('fs');

var gm = require("gm");
var exec = require('child_process').exec;
var IMAGEFILEPATH = "/images";
var gmcreateImage = function() {

var imageConfig = {"topimage":{"density":"300x300","startx":925,"starty":650,"width":575,"height":825},
 "offers": [
          {"startx": 75, "starty": 850, "msg": "SAVE 5$", "textcolor": "#4f61ac", "font": "Arial Bold", "fontsize":34,"stroke":{"color":"#4f61ac","width":4}},
          {"startx": 75, "starty": 970, "msg": "per gallon", "textcolor": "#4f61ac", "font": "Arial Bold", "fontsize":34,"stroke":{"color":"#4f61ac","width":4}},
          {"startx": 75, "starty": 1150, "msg": "With the purchase of", "textcolor": "black", "font": "Arial", "fontsize":18,"stroke":{"color":"black","width":1}},
          {"startx": 75, "starty": 1260, "msg": "any Pepsi Z0 S2", "textcolor": "black", "font": "Arial", "fontsize":16,"stroke":{"color":"black","width":1}},
          {"startx": 75, "starty": 1370, "msg": "on all flavours", "textcolor": "black", "font": "Arial", "fontsize":16,"stroke":{"color":"black","width":1}},
          {"startx": 75, "starty": 1480, "msg": "Ask for details.", "textcolor": "black", "font": "Arial", "fontsize":18,"stroke":{"color":"black","width":1}}
]};
    var addLast=imageConfig.topimage.last;
    var commandStr = "gm convert '-page' '+0+0' '-units' 'PixelsPerInch' '-density' '" + imageConfig.topimage.density + "' '" + IMAGEFILEPATH+ "/template.jpg' ";

    var imageActualPosition={};
    imageActualPosition["x"] = imageConfig.topimage.startx;
    imageActualPosition["y"] = imageConfig.topimage.starty;

    if (!addLast) {
        commandStr += " '-page' '+" + imageActualPosition["x"] + "+" + imageActualPosition["y"] + "' '" + IMAGEFILEPATH + "/top.jpg' ";
    }

    var offers = imageConfig.offers;
    for (var i in offers) {
        var color = offers[i].textcolor;
        var startX = offers[i].startx;
        var startY = offers[i].starty;
        var font = offers[i].font;
        var fontSize = offers[i].fontsize;
        var msg = offers[i].msg;
        var offerStr = "";
        if (offers[i].stroke) {
            offerStr += " '-stroke' '" + offers[i].stroke.color + "' '-strokewidth' '" + offers[i].stroke.width + "'";
        }
        offerStr += " '-fill' '" + color + "' '-pointsize' '" + fontSize + "' '-draw' 'text " + startX + " " + startY + " \"" + msg + "\"'";
        commandStr += offerStr;
    }
    if (addLast) {
        commandStr += " '-page' '+" + imageActualPosition["x"] + "+" + imageActualPosition["y"] + "' '" + IMAGEFILEPATH + "/top.jpg' ";
    }
    var finalImage="done.jpg";
    commandStr += " '-mosaic' '-quality' '100' '" + IMAGEFILEPATH + finalImage + "'";
    exec(commandStr, function(err, stdout, stderr) {
            if (err) {
                console.log("Error while executing gm commands" + err);
                return;
            } else {
                console.log("Done See your image");
            }
    })
};
gmcreateImage();
Antipersonnel answered 29/4, 2015 at 10:3 Comment(2)
This doesn't answer the question. You're not using buffer data to process, you're using a child process combo'd with an existing file. Glad it works for you but it doesn't belong as an answer to this question.Dinin
yes i agree with you this is not the answer of this question . But I just share how I solved this problem in my case. Because as a developer we must have to find the solution may be with different approach. :)Antipersonnel
T
1

As noted in another response, graphicsmagick doesn't allow a buffer as an input. However, interestingly, it does allow an HTTP URL as an input. If you are determined to get this to work without modifying graphicsmagick's source code, try something like this:

setup

Add the uuid module:

npm install uuid

Create a global object to hold the buffers:

const magickbuffers = {}

Create an http server in your script:

const uuid = require('uuid')
const http = require('http')
const magickserver = new http.Server()
const magickport = 9555

magickserver.on('request', (req, res) => {
  res.writeHead(200, { 'Content-Type': 'image/png' })
  if (magickbuffers[req.url]) {
    res.end(magickbuffers[req.url])
  }
  else {
    res.end("\n")
  }
})

magickserver.listen(magickport, () => {})

Create a prototype to supply a usable local url so graphicsmagick can find the buffer on the local server and hand it off as an image, and also to handle the storage of the buffer:

Object.prototype.openBuffer = function() {
  this.id = uuid()
  magickbuffers[`/${this.id}`] = this
  return `http://localhost:${magickport}/${this.id}`
}

Create another prototype to handle the cleanup of the buffer once you're completely done with it:

Object.prototype.closeBuffer = function() {
  delete magickbuffers[`/${this.id}`]
  return true
}

usage

Old example that is desired but doesn't work:

gm()
  .in('-page', '+0+0')
  .in(bufferone) // gm cant use a buffer here, this won't work
  .in('-page', '+50+20')
  .in(buffertwo) // or here
  .mosaic()
  .stream('png', function (err, stdout, stderr) {
    stdout.pipe(writestream)
  })

New method that works:

let one = bufferone.openBuffer() 
let two = buffertwo.openBuffer() 
gm()
  .in('-page', '+0+0')
  .in(one) // gm recognizes this because 'one' is a url pointing to the buffer
  .in('-page', '+50+20')
  .in(two)
  .mosaic()
  .stream('png', function (err, stdout, stderr) {
    stdout.pipe(writestream)
    bufferone.closeBuffer() // do this once gm has completely finished using these buffers
    buffertwo.closeBuffer() // don't forget to do this for each buffer manipulated with .openBuffer()
  })    
Thirzia answered 15/11, 2018 at 1:11 Comment(0)
N
0

I haven't figured out how to do it with both an image and the watermark as buffers, but I have figured out how to keep the image as a buffer:

gm(imageBuffer)
    .composite('./logo_path.png')
    .geometry(geometry)
    .gravity('SouthEast')
    .dissolve(this.options.opacity)
    .toBuffer(function (err, buffer) {
      next(err, buffer, 'image/jpeg');
    });
};

Check out the code in this great library for more info.

Neri answered 7/5, 2016 at 17:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.