Tuesday, April 14, 2009

2 Helpful Script-Fu Resources and More Script-Fu Code

Yesterday, I decided I'd write a quick Script-Fu function that would give me one button saving of a file as a PNG. That way, I can develop the image as a .xcf, and whenever I want to save it as a .png I just hit Control+P. No dialog boxes pop up, it just does the right thing.

While developing this plugin, I made use of two resources:

  • GimpMode - Wow, this is one slick mode for emacs. It gives you tab completion on gimp procedures, and allows you to do interactive development. Easy the installation is cleanly done. It's packages like this that remind you why emacs is still here today. Well done.
  • Docs for the re-match function - I needed a way of turning a filename like foo.xcf into foo.png. One of the easiest ways to do this is with regular expressions. Gimp has the re extension built in, so this should be doable. It took this document explaining how it works before I really go it. Here's a sample usage:
    (define (morph-filename orig-name new-extension)
      (let ((buffer (vector #f #f #f)))
        (if (re-match "^(.*)[.]([^.]+)$" orig-name buffer)
            (string-append (re-match-nth orig-name buffer 1) "." new-extension)
            (string-append orig-name "." new-extension))))
    Notice how I'm passing in buffer, and then using it to extract the matches found. You'll want to also note that the vector has to be sized correctly when passed in.

Here's the completed plugin. One piece of the puzzle that tripped me up is that you can only save a single layer as a PNG. So, to save your image, you need to invoke gimp-layer-new-from-visible, add that new layer to your image with gimp-image-add-layer and then finally, you can pass that new layer to file-png-save-defaults.

(define (bs-save-it-png img drawable)
  (let ((current-name (car (gimp-image-get-filename img)))
        (target (car (gimp-layer-new-from-visible  img img "Save Layer"))))
    (gimp-image-add-layer img target -1)
    (file-png-save-defaults RUN-NONINTERACTIVE img target 
                            (morph-filename current-name "png")
                            (morph-filename current-name "png"))
    (gimp-image-remove-layer img target)))

 "Save-It: PNG"
 "Quickly save the currrent file as a PNG"
 "Ben Simon"
 "Copyright 2009, i2x"
 "April, 2009"
 SF-IMAGE "Image to process" 0
 SF-DRAWABLE "Image to process" 0)

(script-fu-menu-register "bs-save-it-png" "<Image>/Filters")


  1. Awesome. Thanks for continuing to post Script-Fu stuff.

  2. Thanks for the encouragement Grant!


  3. Hello Ben,
    SIOD's re-match-nth does not seem to be available
    in TinyScheme for GIMP 2.6.6 Windows Vista SP2
    or Mac OS X. So I tried for filename-basename

    ; Extract the basename of the original filename
    ; without its extension
    (define (filename-basename orig-name)
    (car (strbreakup orig-name "."))
    ) ; end filename-basename

    The string split function strbreakup is supplied
    in the compatibility file from SIOD to TinyScheme:
    C:\Program Files\GIMP\share\gimp\2.0\scripts\script-fu-compat.init

  4. Nimzo -

    Thanks for the tip.

    I can't believe they dropped re-match-nth...oh well. Thanks for the alternate solution!


  5. Anonymous2:38 PM

    You don't need re-match-nth:

    (define (morph-filename orig-name new-extension)
    (let* ((buffer (vector "" "" "")))
    (if (re-match "^(.*)[.]([^.]+)$" orig-name buffer)
    (string-append (substring orig-name 0 (car (vector-ref buffer 2))) new-extension)

  6. Nice catch -- thanks for sharing!