CUFP
beppu
<div align="center">
(...or "Perl as a Scheme Preprocessor")
</div>
<p>
When people hear that the GIMP can be scripted
using Scheme, they think that it means you can
write scripts to perform large batch operations.
A common question that comes up on the gimp-user
mailing list is, "I have a bunch of images in a
directory - How can I script the GIMP to make a
bunch of thumbnails out of them?" That might
sound easy enough, but it's actually not.
</p>
<p>
The problem is that the Scheme interpreter
within the GIMP has been castrated. All the
functions that allow one to interact with the
underlying operating system have been removed.
That means no forking, no
opendir/readdir/closedir, no nothing.
</p>
<p>
I believe this was done for security purposes.
The GIMP comes with a <b>Script-Fu Server</b> which
lets you send arbitrary Scheme code over a
socket for a running GIMP process to execute.
This Scheme code could be coming from anywhere
on the Internet, so you'd be foolish to let it
do whatever it wanted. To make an analogy with
something familiar to all of us here, imagine
what life would be like JavaScript programmers
could access filesystems on the client side.
</p>
<p>
Basically, it's a nightmare waiting to happen,
so the GIMP developers wisely stripped the
Scheme interpreter of a lot of its power.
Unfortunately, this made Script-Fu a lot less
useful than it could have been. Ironically,
hardly anyone uses the Script-Fu server, so it
may seem like all the work performed to secure
that GIMP was wasted.
</p>
<p>
However, it is through the Script-Fu Server that
we make the GIMP usable for large batch
operations, again. The perl script that follows
lets you send Scheme code to a Script-Fu Server,
but there's an added twist. It also lets you
embed Perl code inside your Scheme, effectively
turning Perl into a Scheme preprocessor.
</p>
<p>
Even if the Scheme interpreter is oblivious to
the operating system that surrounds it, Perl
knows what's up. Perl knows all about @ARGV and
the filesystem and databases and the Internet.
There is no shortage of data sources when Perl
is involved. Now, you actually can write
a Script-Fu program that can batch-create a
bunch of thumbnails... and you can run that
program from the command-line, even. You can do
a lot more than that, too.
</p>
<p>
That's my <b>Cool Use For Perl</b>.
bringing Scheme and Perl together, &&
bringing the GIMP and the command-line together...
They're an odd couple, but they seem to get
along OK.
</p>
<h3>gimp-request</h3>
<tt>
<code>
#!/usr/bin/perl -w
use strict;
use IO::Socket;
use Getopt::Long;
use Text::Template;
sub sexp_from_list {
"(" . join(" ", map { qq("$_") } @_) . ")";
}
sub set_argv {
"(set! argv '" . sexp_from_list(@ARGV) . ")";
}
# defaults
my $verbose = 0;
my $peer_host = "localhost";
my $peer_port = 10008;
GetOptions (
"server|s=s" => \$peer_host,
"port|p=i" => \$peer_port,
);
# connect to the gimp
my $gimp = IO::Socket::INET->new (
Proto => "tcp",
PeerHost => $peer_host,
PeerPort => $peer_port,
);
# preprocess scheme code using perl
my $template;
if (@ARGV) {
$template = Text::Template->new (
TYPE => "FILE",
SOURCE => shift
);
} else {
$template = Text::Template->new (
TYPE => "FILEHANDLE",
SOURCE => \*STDIN
);
}
my $script_fu = $template->fill_in();
$script_fu =~ s/^#.*$//m;
# request
my $length = length($script_fu) & 0xffff;
my $lo_byte = ($length & 0x00ff);
my $hi_byte = ($length & 0xff00) >> 8;
my $header = "G ";
vec($header, 1, 8) = $hi_byte;
vec($header, 2, 8) = $lo_byte;
syswrite($gimp, $_) for ($header, $script_fu);
# response
sysread($gimp, $header, 4);
my @byte = map { ord } split('', $header);
$length = ($byte[2] << 8) | $byte[3];
read($gimp, my $response, $length);
print
"error | $byte[1]\n",
"length | $length\n"
if ($verbose);
print $response, "\n";
exit $byte[1];
__END__
=head1 NAME
gimp-request - send a request to GIMP's Script-Fu Server
=head1 SYNOPSIS
Syntax:
$ gimp-request \
[--server=HOST][--port=PORT] \
[SCHEME_FILE] [ARGS]...
Bang Notation:
#!/usr/bin/env gimp-request
(define beppu `("just"
"another"
{ qw("script-fu") }
"hacker"))
=head1 DESCRIPTION
This is a script for sending a request to a
Script-Fu Server. Before the Scheme code is
sent, it is preprocessed by Perl. Anything
within a pair of curly braces will be treated as
Perl, and you can use this facility to generate
Scheme code.
This is useful, because Perl has access to all
sorts of data that the Scheme interpreter inside
the GIMP does not. It is also safe, because the
Perl code is only executed on the client-side
and all the Script-Fu server will ever see is
pure Scheme code.
=head1 AUTHOR
John BEPPU - beppu@ax9.org
=cut
</code>
</tt>
<p>
PS: gimp-request made its first appearance in the
Feb. 2002 issue of Linux Magazine. I've
been wating since December to post this on
perlmonks.org.
</p>
<p>
PPS: I know I could just use Gimp::Perl which
doesn't suffer from these limitations, but
not everyone has a Perl-enabled GIMP (whereas
all GIMPs have a Scheme interpreter).
</p>