Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.

How Scheme and Perl Became Friends

by beppu (Hermit)
on Feb 20, 2002 at 10:00 UTC ( #146539=CUFP: print w/replies, xml ) Need Help??

(...or "Perl as a Scheme Preprocessor")

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.

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.

I believe this was done for security purposes. The GIMP comes with a Script-Fu Server 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.

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.

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.

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.

That's my Cool Use For Perl. 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.


#!/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 - =cut

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

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).

Replies are listed 'Best First'.
Re: How Scheme and Perl Became Friends
by beppu (Hermit) on Feb 22, 2002 at 17:50 UTC

    /me looks at the speechless monks...

    Here's the example script that I used in the column. It's not great code, but I had time and space limitations that were kicking my ass, but enough excuses... It shows a little Perl being embedded inside a Scheme script, and that's all that counts for now.


    #!/usr/bin/env gimp-request (begin ; This is a helper function that puts ; the contents of @ARGV into the ; Scheme variable, argv. {set_argv} ; This uses perl's regexes to create ; filenames for the thumbnails. Doing ; this in Scheme would be harder. (set! outfiles '{ sexp_from_list( map { s/\..*$/-thumbnail.png/; $_ } @ARGV) }) ; configure your scaling factor (set! scale 0.30) ; This is a function for resizing and saving images (define (resize filename) (let* ((image (car (gimp-file-load 0 filename filename))) (drawable nil) (wd (car (gimp-image-width image))) (hi (car (gimp-image-height image))) (_wd (* wd scale)) (_hi (* hi scale)) (new-filename nil)) (gimp-image-scale image _wd _hi) (set! drawable (car (gimp-image-flatten image))) (set! new-filename (car outfiles)) (set! outfiles (cdr outfiles)) (gimp-file-save 1 image drawable new-filename new-filename) (gimp-image-delete image) )) ; Finally, make a thumbnail out of every file in argv (for-each resize argv) )
Re: How Scheme and Perl Became Friends
by beppu (Hermit) on Feb 23, 2002 at 05:27 UTC

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: CUFP [id://146539]
Approved by root
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others cooling their heels in the Monastery: (4)
As of 2023-09-25 06:37 GMT
Find Nodes?
    Voting Booth?

    No recent polls found