Search code examples
perlsvgautomationcommand-line-interfaceinkscape

Using the Inkscape shell from perl


Inkscape has a shell mode invoked like this

inkscape --shell

where you can execute commands like this:

some_svg_file.svg -e some_png_output.png -y 1.0 -b #ffffff -D -d 150 

which will generate a PNG file, or like this:

/home/simone/some_text.svg -S

which gives you the bounding box of all elements in the file in the return message like this

 svg2,0.72,-12.834,122.67281,12.942
 layer1,0.72,-12.834,122.67281,12.942
 text2985,0.72,-12.834,122.67281,12.942
 tspan2987,0.72,-12.834,122.67281,12.942

The benefit of this is that you can perform operations on SVG files without having to restart Inkscape every time.

I would like to do something like this:

sub do_inkscape {
     my ($file, $commands) = @_;
     # capture output
     return $output
}

Things work OK if I use open2 and forking like this:

use IPC::Open2;

$pid = open2(\*CHLD_OUT, \*CHLD_IN, 'inkscape --shell');
$\ = "\n"; $/ = ">";

my $out; open my $fh, '>', \$out;

if (!defined($kidpid = fork())) {
    die "cannot fork: $!";
} elsif ($kidpid == 0) {
    while (<>) { print CHLD_IN $_; }
} else { 
    while (<CHLD_OUT>) { chop; s/\s*$//gmi; print "\"$_\"";  }
    waitpid($kidpid, 0); 
}

but I can't find out how to input only one line, and capture only that output without having to restart Inkscape every time.

Thanks

Simone


Solution

  • You don't need to fork, open2 handles that by itself. What you need to do is find a way of detecting when inkscape is waiting for input.

    Here's a very basic example of how you could achieve that:

    #! /usr/bin/perl
    use strict;
    use warnings;
    
    use IPC::Open2;
    
    sub read_until_prompt($) {
        my ($fh) = (@_);
        my $done = 0;
        while (!$done) {
            my $in;
            read($fh, $in, 1);
            if ($in eq '>') {
                $done = 1;
            } else {
                print $in;
            }
        }
    }
    
    my ($is_in, $is_out);
    my $pid = open2($is_out, $is_in, 'inkscape --shell');
    
    read_until_prompt($is_out);
    print "ready\n";
    
    print $is_in "test.svg -S\n";
    read_until_prompt($is_out);
    
    print $is_in "quit\n";
    waitpid $pid, 0;
    
    print "done!\n";
    

    The read_until_prompt reads from inkscapes output until it finds a > character, and assumes that when it sees one, inkscape is ready.

    Note: This is too simple, you will probably need more logic in there to make it work more reliably if a > can appear outside the prompt in the output you're expecting. There is also no error checking at all in the above script, which is bad.