Script-Fu Programmer's Reference
This describes version 3 of Script-Fu, a language for scripting or writing plugins for the GIMP application.
The Script-Fu language is interpreted. An embedded interpreter underlies each plugin written in Script-Fu. Architecturally, the Script-Fu interpreter wraps another interpreter, the TinyScheme interpreter.
This is a reference. You can find a definition of what is a GIMP plugin and on what Script-Fu differs from other interpreted plugins on About Plug-ins and Filters.
Versions
Script-Fu itself is not versioned and has not changed much in GIMP version 3, but the bound PDB API has.
A document Changes in ScriptFu v3 explains the changes needed to port a script from GIMP version 2.X to 3.
References to underlying languages
Script-Fu language is a dialect or extension of underlying languages. The language descends from TinyScheme from Scheme from Lisp.
This documents what is atop or special or different from the underlying languages. See also:
ScriptFu more or less conforms to the R5RS standard, with the notable exception that it lacks hygenic macros.
Features of the Script-Fu language
These are features beyond simple Scheme or TinyScheme:
- special functions to declare and define a GIMP plugin
- binding to the GIMP PDB procedures for processing images
- many pre-defined symbols
- Unicode strings
- i18n internationalization/localization of any GUI
- ftx extension for filesystem operations
- re extension for regular expressions
- byte IO
- a few functions not in Scheme, defined in the usual init.scm for a Scheme interpreter
- other Scheme functions whose meaning is implementation dependant (e.g. quit)
Script-Fu special functions
These declare a plugin to the PDB. These functions are not in the Scheme language proper, and not in the PDB. They are implemented in C language and bound as foreign functions to Scheme. The special functions are:
script-fu-register: this is deprecatedscript-fu-register-procedurescript-fu-register-filterscript-fu-menu-registerscript-fu-register-18n(since GIMP v3.2)
The first argument to these functions is a string and
it is the declaration of a plugin’s signature.
The declaration of a plugin’s signature
describes both the signature registered in the PDB
and the signature of the run function defined in Scheme at
the top of a script, and it should be the same for both.
The rest of the arguments after the signature are metadata arguments.
The signature of the run function in a Script-Fu script does not have a run mode argument. Script-Fu hides it from a script since a script should not implement GUI directly. Only if it calls other plugins (in the PDB), it should usually pass run mode NON-INTERACTIVE so the called plugin does not present its dialog.
script-fu-register-procedure
Use script-fu-register-procedure to declare PDB procedures that are not filters or renderers. “Procedure” denotes the general case. Plugins that are filters or renerers, taking an image and drawables, are special and you instead declare them using script-fu-register-filter.
script-fu-register-procedure declares a script that:
- is always enabled
- can save its settings between sessions
Here is an abbreviated example:
(define script-fu-my-plugin (radius)
body)
(script-fu-register-procedure "script-fu-my-plugin"
"My plugin..."
"Example plugin."
"author/copyright holder"
"copyright dates"
SF-ADJUSTMENT "Radius" (list 100 1 5000 1 10 0 SF-SPINNER)
)
script-fu-menu-register
Another example. A plugin may define a menu item appearing in a context menu. This is a common use case for script-fu-register-procedure. The context is the set of choices in GIMP that affect drawing operations, for example, the current Brush.
Note that the plugin does NOT receive the choice in context but must get it from the context before operating with/on it.
Using the same registration as above:
(define script-fu-my-plugin (radius)
; do something with the current brush
(gimp-context-get-brush))
(script-fu-register-procedure "script-fu-my-brush-plugin"
...same registration as above...)
(script-fu-menu-register "script-fu-my-brush-plugin"
"<Brushes>/Brushes Menu")In this example, the menu item “My plugin…” appears in the context menu (pops up with right mouse button) in the Brushes dockable window, and is always enabled. The script run func gets the current brush and does something to or with it.
One more example. A plugin may want to use a file that is not open as an image.
(define script-fu-my-plugin (filename)
; open and do something with the file
)
(script-fu-register-procedure "script-fu-my-plugin"
"My plugin..."
"Example plugin."
"author/copyright holder"
"copyright dates"
SF-FILENAME "Image to do something with" ""
)
(script-fu-menu-register "script-fu-my-plugin"
"<Image>/File")In this example, the menu item My plugin… appears in the File menu on the menubar. It is always enabled.
When a user chooses the menu item, a dialog appears that lets a user choose a file. When the user clicks OK, the plugin opens the file and does something with it.
script-fu-register-filter
Use script-fu-register-filter to declare a PDB procedure that take an image and drawable components of images.
script-fu-register-filter declares a script that:
- is a filter or renderer: taking a user selected image and its selected components
- is multi-layer capable, processing one or more drawables
- can save its settings between sessions
- has a menu item that is enabled/sensitized when a user selects image components
The run func that you define in your script must have those formal arguments (and the PDB Browser will show those arguments). For example:
(define script-fu-my-plugin (image drawables arg1 arg2) body)The Script-Fu binding to the PDB
The binding from Scheme to GIMP is part of the Script-Fu system.
Unlike bindings for other languages of plugins (such as the Python binding) the binding is not automatic using GObject Introspection (GIR.) Instead, the binding is mostly hand-coded in C.
The binding is to the PDB API, instead of the libgimp API. The binding defines a symbol into the Scheme language for each procedure in the GIMP PDB. The binding also marshalls arguments to and return values from calls to the PDB and marshalls C types to Lisp types and vice versa. That way, a Script-Fu plugin script can call a PDB procedure.
- On a successful call to the PDB, Script-Fu binds the normal returned values into a Scheme data structure. The call yields that Scheme data structure, which is a list in v2 dialect and the single value itself on v3 dialect. See: Using the version 3 dialect for PDB values
- On a failed call to the PDB, the calling script usually does not continue. Instead, the plugin returns, propagating the error. In Scheme terminology, the script’s call to the PDB throws a Scheme exception, which if not caught calls the error function which terminates the plugin script that is calling the PDB. See also Exceptions and the *error-hook*
This table shows the corresponding Scheme type for each declared C type in the PDB documentation.
| Declared C type | Scheme type |
|---|---|
| int | Integer |
| double | Float |
| char* | String |
| gboolean | #t or #f |
| GStrv | List of String |
| Gimp objects e.g. GimpImage | Integer (ID) |
| GimpCoreObjectArray | Vector of Numeric (ID) |
To understand the signatures of Script-Fu’s binding of calls to the PDB, read the C signatures in the Procedure Browser and mentally convert to a Scheme type. For example, a C integer converts to a Scheme numeric and a C string converts to a Scheme string.
The document Adapting scripts to PDB version 3 describes other conversions from GIMP types to Scheme types.
Keyword arguments in calls to plugins in the PDB
On v3 scripts, you should call plug-in procedures using keyword syntax.
Keyword syntax is also known as “named arguments” so all arguments must have names, not just trailing arguments. The syntax is like the syntax from other Scheme implementations. The characters “#:” precede the name of the argument (as given in the PDB API). Example:
(plug-in-tile #:run-mode RUN-NONINTERACTIVE
#:image testImage
#:drawables testLayers
#:new-width 10
#:new-height 20
#:new-image #f)Using keyword arguments is optional, but it is all-or-nothing within a call: all arguments must have keywords, or none.
Pre-defined symbols
Script-Fu defines many symbols over and above TinyScheme and Scheme.
These symbols are immutable and you cannot globally redefine them although you could redefine them in a let block.
Pre-defined symbols for GIMP constants i.e. enumerations
Specially, the Script-Fu language defines symbols for each enumeration value of the GIMP library and the GEGL library. You often pass these symbols as arguments to calls to the PDB. These symbols are in upper case. These symbols match the symbols in the respective libraries, except they omit the “GIMP_” or “GEGL_” prefix.
For example, RUN-INTERACTIVE is the symbol for one of the enumerated values in the GimpRunMode enumeration.
Script-Fu specific pre-defined symbols for constants
Script-Fu defines these symbols:
TRUE: alias for 1FALSE: alias for 0NULL: alias for 0
Symbols for strings about the filesystem:
gimp-directory: the path to the directory where GIMP is installed, aka PREFIXgimp-data-directorygimp-plug-in-directorygimp-locale-directorygimp-sysconf-directoryscript-fu-sys-init-directory: the directory containing init.scm that ScriptFu readsscript-fu-user-init-director: the directory where users install .scm files read at init timeDIR-SEPARATOR: the ASCII character used as separator in filesystem paths, e.g. forward slashSEARCHPATH-SEPARATOR: the ASCII character used as separator in PATH lists, e.g. colon
The SF- enumeration of IN argument types
Script-Fu defines symbols used to declare types of arguments to a plugin.
You declare each IN argument in a sequence of three values,
and you can declare many arguments.
For example, SF-ADJUSTMENT:
(script-fu-register-procedure "script-fu-test-sphere"
... metadata arguments...
SF-ADJUSTMENT "Radius (in pixels)" (list 100 1 5000 1 10 0 SF-SPINNER)
... more triples, one for each additional argument ...
)The three values in a triple are:
- a SF- constant (see below)
- a string label to appear alongside a widget in the plugin’s dialog
- a value that defines a default or further constrains the argument (a list for some argument types)
The SF- enumeration declares not only the type of the argument,
but the kind of widget used in a dialog for the plugin.
For example, Script-Fu defines symbols SF-SLIDER and SF-SPINNER used to declare widget types
for arguments of type SF-ADJUSTMENT.
SF-BRUSH, SF-FONT, SF-GRADIENT, SF-PALETTE, and SF-PATTERN
all specify an argument of type GIMP Resource.
Resources are data installed with GIMP.
Similarly,
SF-IMAGE, SF-LAYER, SF-CHANNEL, SF-DRAWABLE, and SF-VECTORS
all specify an argument having one of GIMP’s object type.
When you declare an argument having the type of a GIMP object,
the third argument is usually just an ignored place holder “-1”.
In ScriptFu, resources and objects are represented by their integer ID’s. A script should treat these opaquely, receiving them and passing them unaltered to other functions.
The SF- enumeration of plugin capabilities
Since GIMP version 3, a plugin can declare its capabilities
re whether it can process multiple drawables (usally layers) with
an argument called “multilayer-capability” (SF-*-DRAWABLE).
Then a plugin’s menu item is enabled in the GIMP GUI
only when the user has selected an appropriate count of drawables.
script-fu-register-filter.The argument follows the “image types” argument, which declares the capability of the plugin to process various image modes. Here is an example:
(script-fu-register-filter "script-fu-test-sphere-v3"
"Sphere v3..."
"Test script-fu-register-filter: needs 2 selected layers."
"authors"
"copyright holders"
"copyright dates"
"*" ; image modes: any
SF-TWO-OR-MORE-DRAWABLE ; multi-layer capability argument
SF-ADJUSTMENT "Radius" (list 100 1 5000 1 10 0 SF-SPINNER)
)The “multilayer-capability” argument can have the following values:
SF-ONE-DRAWABLE: expects exactly one drawable (will still receives a vector of drawables, but the vector should be of length one)SF-ONE-OR-MORE-DRAWABLE: expects and will process one or more drawables (typically means a script will filter the given drawables independently and sequentially)SF-TWO-OR-MORE-DRAWABLE: expects and will process two or more drawables (typically means a script will combine the given drawables, say into another drawable by a binary operation)
This is only a declaration, a contract; whether your defined run func does what it promises is another matter. A well-written script should throw an error when it is not passed the declared number of drawables, either more or fewer than declared. See: Respect multi-selected layers
Unicode support
Unlike some Scheme dialects and unlike the original TinyScheme, the Script-Fu interpreter implements Unicode strings. All data of string type in Script-Fu are sequences of Unicode characters, i.e. wide characters.
Script-Fu encodes Unicode character using UTF-8, so a character is one to three bytes. The length of a string is the length in characters, not bytes. See: string length
In a Lisp script, you represent certain literals by a sharp constant. In Script-Fu, you can represent Unicode characters by sharp character constants :
#\a ;represents lower case ASCII "a".You can also represent Unicode characters by sharp numeric constants, for example:
#\x3bb ;represents the two byte encoding of the Greek character called lambda, whose codepoint is hexadecimal 3bb.Symbols are not Unicode but ASCII. (Here symbol has the Lisp meaning of a name in the script.) So a script’s text must be ASCII. Evaluation of string data that is Unicode might not work.
i18n internationalization/localization
In a Script-Fu plugin script, you can annotate with _ a string literal for translation:
_"To be translated."Any string literal so annotated will usually appear in the user’s native language in the GUI dialog for a plugin.
Script-Fu does not support translation of strings generated at run time. For internationalization to succeed, a translator must translate annotated strings, into a .po file. At install time, the build system must compile and install the translations. See Internationalizing GIMP Scheme Plugins for more information.
ftx and re extensions
Script-Fu includes the ftx (OS and filesystem) and re (regular expression) extensions. An extension in Scheme is C code that defines more functions into the language.
See the gimp repo for more information:
Byte IO
Since characters are Unicode in Script-Fu, you can’t use characters as bytes. Also, Byte IO is not part of the RSR5 standard.
Since version 3, however, ScriptFu has functions to read and write streams a byte at a time. See: Type byte
Other defined functions
Script-Fu has some functions not in Scheme and not specific to GIMP. Some are implemented in the TinyScheme interpreter itself, and some in Scheme scripts in the usual init.scm for a Scheme interpreter.
The print function
is not in RSR5 but is defined in ScriptFu.
The quit function is usually defined in Scheme but with implementation
dependent meaning re a process.
The cond-eval function is defined but in a limited way.
That function lets you evaluate conditionally on the prior loading of other Scheme modules
following the SRFI conventions.
The Script-Fu interpreter
If you are an “advanced” user, who might want to understand the implementation of some built-in Scheme function, this loosely describes how the interpreter works.
Initilization of the interpreter
We use the words “Script-Fu loads” to mean similar to the Scheme load function, which reads a script file and evaluates it. Often a loaded file contains only definitions, i.e. calls to “(define …)”, which is similar to an import in other languages. But a loaded file may have more than just defines. See the special case for Script-Fu below.
-
Script-Fu initializes the embedded TinyScheme interpreter. This defines core Lisp functions into the interpreter state. The embedded TinyScheme interpreter and these core functions are written in C.
-
Script-Fu makes extensions ftx and re define their functions into the interpreter. These extensions are in C.
-
Script-Fu defines its special functions into the interpreter. The special functions are written in C.
-
Script-Fu introspects or queries the PDB and defines into the interpreter each procedure in the PDB. A Scheme call to the PDB passes through wrapper functions written in C.
-
Script-Fu defines into the interpreter aliases for PDB procedures whose name has changed from prior versions of GIMP. A table in C associates alias names to new names. For backward compatibility, only in Script-Fu are the old names deprecated, i.e. aliased and still useable, and this might change in the future.
-
Script-Fu loads all the scripts in the directory
/scripts/script-fu-init. This directory is part of a GIMP installation. This directory always contains the conventional script file:init.scm, as in other Scheme implementations. The file init.scm defines more core functions into the interpreter, such as cadr. This step is typical of Lisp interpreters. But the file is slightly altered from the usual TinyScheme init.scm. -
Script-Fu loads
/scripts/script-fu-init/script-fu-compat.scmThis file is a Scheme script. For backward compatibility, it might define certain deprecated Scheme functions from earlier versions of Script-Fu that used the SIOD dialect of Lisp (not anymore on GIMP 3.0). It also defines the not-deprecated random function for random number generation. -
Script-Fu loads
/scripts/script-fu-init/plug-in-compat.scmThis file is a Scheme script. For backward compatibility, it might define functions for certain deprecated PDB procedures from earlier versions of the GIMP PDB (not anymore on GIMP 3.0). The functions are written in Scheme and wrap calls to newer PDB procedures. They are not just aliases for renamed PDB procedures, the wrappers may do more or convert signatures. -
Script-Fu loads each script file in two
/scriptsdirectories, the user owned /scripts directory and the system-wide /scripts directory. This defines (temporarily) into the interpreter state all the PDB functions that are old-style plugin written in the Script-Fu language (thanks to the extension protocol) as well as any other defined functions in those files!So, any Scheme files (.scm) in those directories that do not define plugins can never-the-less define into the interpreter state. For example, the file /scripts/script-fu-util.scm defines several “utility” functions into the interpreter state. Thus every instance of the Script-Fu interpreter locally has the body of the functions defined in those files. In other words, script files in those directories can serve as libraries.
Errors
Plugins written in Script-Fu language do not return a value, other than an error status, because Script-Fu discards the value of the last evaluation in the run function. So, a Script-Fu plugin returns a success error status unless
- the interpreter throws an error in Scheme code (see many kinds of interpreting-related errors below)
- or a called PDB procedure returns an error (see “Run-time PDB” below)
- or the plugin calls the quit or error functions (see “Script data” below)
There are some errors only detected in newly installed scripts, i.e. at query time:
-
Syntax Discovered at query time i.e. after the first install, by the embedded TinyScheme interpreter. For example, unmatched parenthesis.
-
Registration Discovered at query time i.e. after the first install, by Script-Fu. The data for registration in a call to script-fu-register* is flawed, e.g. not a sequence of triplets.
Other error kinds will not be detected until run time of the plugin’s run function, when a user invokes the plugin.
-
Binding Discovered at run time, by the embedded TinyScheme interpreter. A name i.e. symbol is not bound. Typically a typo. Or, a function name that is in Scheme but not TinyScheme, or a named procedure that is no longer in the PDB. Also known as “unbound variable.”
-
Scheme Type Discovered at run time, by the embedded TinyScheme interpreter. Some Scheme functions dynamically check the type of their arguments.
-
PDB Type Discovered at run time, by the Script-Fu wrapper. Script-Fu checks the Scheme type of arguments in calls to a PDB procedure, and the PDB checks the C type of arguments in calls to a PDB procedure. The PDB also range checks some arguments. Also know as a “calling error.”
-
Run-time PDB Discovered at run time, by a called PDB procedure. For example, passed an image type that the procedure cannot handle. Also known as an “execution error.”
-
Script data Discovered at run time, by a script, in some data. A script should call throw, quit, or error to declare such an error. See: Messaging functions
-
System error Discovered at run time, by the OS or by the embedded TinyScheme interpreter. For example, a runaway script exhausted memory allocated by the interpreter, or a file operation failed.
Exceptions and the *error-hook*
Errors are usually fatal i.e. stop evaluation of a plugin script.
Errors are Scheme exceptions.
On an exception, the interpreter unwinds call frames to the first catch Scheme function
or finally calls the error Scheme function which is fatal.
An example of a script that uses catch is contact-sheet.scm
which catches file opening errors and ignores them.
They symbol *error-hook* is a hook in the embedded TinyScheme interpreter.
ScriptFu has a standard definition of the *error-hook* symbol on the usual init.scm
script, named script-fu.init. The standard definition is equivalent to just Scheme throw.
Advanced users can redefine the *error-hook* symbol. Typically used in test frameworks. When they do, the behavior of the error function can be changed by the new *error-hook*. A call to error generates an exception which the *error-hook* catches.
(There are other hook symbols in the embedded TinyScheme interpreter, e.g. *sharp-hook* which you can redefine to alter the parsing of sharp constants.)
Old and new styles of Script-Fu plugins
After reading Features of the Script-Fu language, you are ready to write a Script-Fu plugin.
Since the language is interpreted, there is no compilation step. There is an install and registration step. Installation is just copying a script file to a certain directory. Registration happens when GIMP runs and queries the files in the directory.
There are two styles of Script-Fu plugins that can be installed and registered:
Old-style plugins (in version 2 and version 3):
- are installed scattered in
/scripts - are served by the Script-Fu extension process (that can crash cripple the GIMP app)
- call deprecated script-fu-register, declaring all arguments
- have a limited GUI implemented by the Script-Fu system
New-style plugins (only in version 3):
- are installed in
/plug-inslike other plugins - are interpreted each in a separate process
- can call script-fu-register-filter, not declaring standard args
- have a GUI provided by the GimpProcedureDialog class of libgimp
Installation of such new-style scripts must follow rules for interpreted plugins. A script file must:
-
be in a directory having the same base name, for example:
~/.config/GIMP/0.0/plug-ins/script-fu-my/script-fu-my.scmIt is only a convention to name scripts starting with “script-fu-”. -
have a file suffix corresponding to an interpreter (in this case,
.scm) -
have executable permission (relevant for Unixes like Linux and macOS)
-
have a shebang on the first line, for example:
Scheme#!/usr/bin/env gimp-script-fu-interpreter-3.0 ;!# Close comment started on first line.If the script has translatable strings, it should also have a second line as above, to accomodate parsing by gettext. (Note that ScriptFu doesn’t actually support multi-line comments using #!, and this is a trick to fool gettext, which parses a script as if it did support multi-line comments.)
The advantage of installing a script in the /plug-ins directory is that, as said, such a script will execute in its own process. If it crashes, it doesn’t affect GIMP or other scripts.
Templates for a Script-Fu plugin
Minimal, hello-world plugin
This is a minimal old-style plugin (not recommended for new scripts):
(define (script-fu-basic-plug-in) (gimp-message " hello world "))
(script-fu-register "script-fu-basic-plug-in" "Basic" "test" "me" "free" "2023" "")
(script-fu-menu-register "script-fu-basic-plug-in" "<Image>/Fu-plug-in")The first line defines the “run function” of the plugin. Here the run function has no arguments and just says “hello world.” You would craft a run function to do something useful.
The second and third lines are special functions of Script-Fu.
The second line declares metadata and the signature of the plugin. Here, the plugin has no arguments, and empty signature.
The third line declares the plugin’s appearance in the GIMP app menus, which is optional.
This text would be in a file named “hello-world.scm” installed in the /scripts directory.
Version 3 filter plugin
This is a minimal plugin in the new style of version 3:
#!/usr/bin/env gimp-script-fu-interpreter-3.0
(define (script-fu-basic-plug-in
image
drawables)
(gimp-message " hello world "))
(script-fu-register-filter "script-fu-basic-plug-in" "Basic" "test" "me" "free" "2023" ""
SF-ONE-DRAWABLE)
(script-fu-menu-register "script-fu-basic-plug-in" "<Image>/Fu-plug-in")One difference is the shebang in the first line. This means the standalone Script-Fu interpreter will interpret the script, independently from other plugins.
Another difference is the run function has formal arguments for the standard arguments for a filter: image and drawables, but the call to script-fu-register-filter does not declare them.
Another difference is the call to script-fu-register-filter has the argument SF-ONE-DRAWABLE to specify the capability of the plugin re multi layers. This example is contrived, because the plugin actually ignores the drawables and actually could work no matter how many layers the user selected.
This text would be in a file named “hello-world.scm” installed in the /plug-ins/hello-world directory (a directory having the same name as the plugin script file.) The file must have executable permissions.
Other example plugins
A more elaborate hello-world in the Gimp repo illustrates a version 3 plugin, installed to the /plug-ins directory and independently interpreted, but not registering as a filter.
A script in the Gimp repo illustrates all types of arguments that you can declare in the signature of a plugin and demonstrates the corresponding widgets.