4

I have certain limitations that I won't specify that require me to use ImageMagick as a child-process.

I have multiple base 64 strings of jpg files which I want ImageMagick to process. Specifically I want ImageMagick to join the jpg files together. If I had 2 regular jpg files then from the command line I would use the following format.

node convert in_1.jpg in_2.jpg +append out.jpg

in a js file I would use

var spawn, magicCommands, imagic;
spawn = require('child_process').spawn;
magicCommands = ["in_1.jpg",
                 "in_2.jpg",
                 "+append",
                 "out.jpg"];
imagic = spawn("convert", magicCommands);

Now if I wanted to use 1 stdin buffer the following would work

    var arrow1JpgBase64, arrow2JpgBase64, arrowBuffer1, arrowBuffer2, magicCommands, imagic;

    spawn = require('child_process').spawn;
    exec = require('child_process').exec;

    arrow1JpgBase64 = "/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCAAFAAkDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD1/wCFngjxJba78QbWT4reLTLH4pZ2mjt9PLTCSxs5VL+bbSYZUkWPCFUxGu1EHFdz/wAIb4j/AOiseM//AAF0n/5BoooA/9k=";
    arrow2JpgBase64 = "/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCAAFAAkDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD1/wCOvgjxJc/Dt7NPit4tMt1q+k20Ty2+nqsLyajbIkoMFtFJuRmDjbIpyo5xmu5/4Q3xH/0Vjxn/AOAuk/8AyDRRQB//2Q==";
    arrowBuffer1 = new Buffer(arrow1JpgBase64, 'base64');
    arrowBuffer2 = new Buffer(arrow2JpgBase64, 'base64');
    magicCommands = ["jpg:",
                     "in_2.jpg",
                     "+append",
                     "out.jpg"];

    imagic = spawn("convert", magicCommands);
    imagic.stdin.write(arrowBuffer1);
    imagic.stdin.end();

    imagic.on('exit', function (code) {
        if (code === 0) {
            exec("open out.jpg");
        } else {
            console.log("error code: " + code);
        }
    }); // end of on exit

So far so good, but I want to use both of the buffers and not just 1 of them. So if I replace the "in_2.jpg", line with "jpg:", then how do I have to change the rest of the script to get it to work?

Thanks

Trevor
  • 525
  • 2
  • 6
  • 19
  • 1
    Gotta dash, but maybe you can use the `fd:` handler in ImageMagick and put one buffer on `fd:3` and another on say `fd:4`... http://www.imagemagick.org/Usage/files/#fd – Mark Setchell Nov 11 '15 at 17:16
  • If you already have a base64 string (and that's < 5000 characters), way not [pass it as an argument directly](http://www.imagemagick.org/Usage/files/#inline) w/ `inline:data:image/jpeg;base64,...`? – emcconville Nov 11 '15 at 19:15
  • @emcconville I was trying to do that same thing myself but it seems not to work any more, I tried Kurt's example here... http://stackoverflow.com/a/12067952/2836621 and the example under **ASIDE** here... http://www.imagemagick.org/Usage/files/#inline It all seems broken in IM v6.9.2-5. – Mark Setchell Nov 11 '15 at 20:53
  • Thanks for the replies. @Mark Setchell, your reply looks very promising but can you write some code showing how to assign an fd to a buffer, sorry for showing my ignorance! – Trevor Nov 11 '15 at 21:56
  • @emcconville, your reply would be ideal but the 5000 char limit is too restrictive. – Trevor Nov 11 '15 at 21:56
  • Another option may be to create a fifo, with `mkfifo` and write your base64 string into the fifo and make IM read one file from `stdin` and the other from a fifo... `convert jpg: fifo -append result.jpg` – Mark Setchell Nov 11 '15 at 22:18
  • @MarkSetchell I shall look into that tomorrow, (I actually need to process an unknown amount of strings so I would have to make a fifo for all of them except the 1st). Your 1st reply sounded very promising, see this [link](http://www.imagemagick.org/discourse-server/viewtopic.php?t=13100) I just don't have a clue how to apply it in practice. Any sample code? or reason not to go that way? – Trevor Nov 11 '15 at 22:38

2 Answers2

4

As Mark Setchell pointed out in the comments, using ImageMagick's fd: protocol will work.

var spawnOptions = {
      stdio: [
        0, // stdin,
        1, // stdout
        2, // stderr
        'pipe', // arrowBuffer1
        'pipe'  // arrowBuffer2
      ]
};
magic = spawn("convert", magicCommands, spawnOptions);

This opens fd:3 & fd:4 for piping. I'm not failure with the family, but there usually a way to pass a resource in addition to .

For your code

Update the magickCommands variable to read from new fd's, and write directly to new pipes

magicCommands = ["fd:3",
                 "fd:4",
                 "+append",
                 "out.jpg"];

// ...
imagic.stdio[3].write(arrowBuffer1);
imagic.stdio[4].write(arrowBuffer2);
imagic.stdio[3].end();
imagic.stdio[4].end();
emcconville
  • 23,800
  • 4
  • 50
  • 66
  • I am getting a message that imagic.stdio[3] is undefined and I can't write to it. I think though this is getting close – Trevor Nov 12 '15 at 16:20
1

Not really sure what these "limitations" are, or what we are trying to avoid or work around, but the following technique of streaming multiple files into one may help you:

# Make a red block
convert -size 50x50 xc:red multi.miff

# Make a green block, but APPEND INTO A SINGLE STREAM
convert -size 50x50 xc:lime miff:- >> multi.miff

# Make a blue block, but APPEND INTO A SINGLE STREAM
convert -size 50x50 xc:blue miff:- >> multi.miff

# Tell IM to convert and append multiple images in single stream 
convert multi.miff +append result.png

enter image description here

Also, if you change the last command above to the following, IM will delete the file multi.miff as soon as it has finished with it - i.e. tidy up for you!

convert ephemeral:multi.miff +append result.png
Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • Thanks again, the limitations I was referring to is that I can't use native (c++ based) node modules, so that excludes me using lwip or other possible options. What I am really looking for is to avoid unnecessary writing to the hard disk. The buffers already exist so if I could just pass them as arguments it would be better than saving them as files and then deleting the files afterwards. I shall post another tread about getting an fd of a buffer if there is such a thing! – Trevor Nov 12 '15 at 08:11
  • 1
    Ok, I am getting closer to understanding I think... have you considered a small RAMdisk for temporary files? – Mark Setchell Nov 12 '15 at 09:59
  • Nope, it's for a clients system, for multiple installs. I shall probably just resort to creating and erasing the file. But I'll first have a go at creating buffer streams and the I think they can be referred to by their fd which I think they have. I posted a post here http://www.imagemagick.org/discourse-server/viewtopic.php?t=13100 but don't understand the answer – Trevor Nov 12 '15 at 13:08
  • I experimented with the ram disk and I looks like it could be a simple and efficient solution. I shall use that method and see it my client's IT department has any problem with it. I'll get back with a report on how it goes. – Trevor Nov 12 '15 at 16:45