GIMP Scheme Error: ( : 1) eval: unbound variable: strbreakup - scheme

I found this wonderful script on github and used it successfully with GIMP on Windows 7. I recently upgraded to Windows 10, and now it will not work. I get the following error:
Error while executing script-fu-batch-smart-resizer:
Error: ( : 1) eval: unbound variable: strbreakup
Here is the code:
; https://github.com/per1234/batch-smart-resize
(define (script-fu-batch-smart-resize sourcePath destinationPath filenameModifier outputType outputQuality maxWidth maxHeight pad padColor . JPEGDCT)
(define (smart-resize fileCount sourceFiles)
(let*
(
(filename (car sourceFiles))
(image (car (gimp-file-load RUN-NONINTERACTIVE filename filename)))
)
(gimp-image-undo-disable image)
;crop to mask if one exists
(if (not (= (car (gimp-layer-get-mask (car (gimp-image-get-active-layer image)))) -1)) (plug-in-autocrop RUN-NONINTERACTIVE image (car (gimp-layer-get-mask (car (gimp-image-get-active-layer image))))))
;image manipulation
(let*
(
;get cropped source image dimensions
(sourceWidth (car (gimp-image-width image)))
(sourceHeight (car (gimp-image-height image)))
;don't resize image to larger than original dimensions
(outputMaxWidth (if (< sourceWidth maxWidth) sourceWidth maxWidth))
(outputMaxHeight (if (< sourceHeight maxHeight) sourceHeight maxHeight))
(outputWidth (if (< (/ sourceWidth sourceHeight) (/ outputMaxWidth outputMaxHeight)) (* (/ outputMaxHeight sourceHeight) sourceWidth) outputMaxWidth))
(outputHeight (if (> (/ sourceWidth sourceHeight) (/ outputMaxWidth outputMaxHeight)) (* (/ outputMaxWidth sourceWidth) sourceHeight) outputMaxHeight))
)
(gimp-image-scale image outputWidth outputHeight) ;scale image to the output dimensions
;pad
(if (= pad TRUE)
(begin
(gimp-image-resize image maxWidth maxHeight (/ (- maxWidth outputWidth) 2) (/ (- maxHeight outputHeight) 2)) ;resize canvas to to maximum dimensions and center the image
;add background layer
(let*
(
(backgroundLayer (car (gimp-layer-new image maxWidth maxHeight RGB-IMAGE "Background Layer" 100 NORMAL-MODE))) ;create background layer
)
(let*
(
(backgroundColor (car (gimp-context-get-background))) ;save the current background color so it can be reset after the padding is finished
)
(gimp-context-set-background padColor) ;set background color to the padColor
(gimp-drawable-fill backgroundLayer 1) ;Fill the background layer with the background color. I have to use 1 instead of FILL-BACKGROUND because GIMP 2.8 uses BACKGROUND-FILL.
(gimp-context-set-background backgroundColor) ;reset the background color to the previous value
)
(gimp-image-insert-layer image backgroundLayer 0 1) ;add background layer to image
)
)
)
)
(gimp-image-flatten image) ;flatten the layers
(let*
(
;format filename - strip source extension(from http://stackoverflow.com/questions/1386293/how-to-parse-out-base-file-name-using-script-fu), add filename modifier and destination path
(outputFilenameNoExtension
(string-append
(string-append destinationPath "/")
(unbreakupstr
(reverse
(cdr
(reverse
(strbreakup
(car
(reverse
(strbreakup filename (if isLinux "/" "\\"))
)
)
"."
)
)
)
)
"."
)
filenameModifier
)
)
)
;save file
(cond
((= outputType 0)
(let*
(
(outputFilename (string-append outputFilenameNoExtension ".png")) ;add the new extension
)
(file-png-save RUN-NONINTERACTIVE image (car (gimp-image-get-active-drawable image)) outputFilename outputFilename FALSE 9 TRUE FALSE FALSE TRUE TRUE)
)
)
((= outputType 1)
(let*
(
(outputFilename (string-append outputFilenameNoExtension ".jpg")) ;add the new extension
)
(file-jpeg-save RUN-NONINTERACTIVE image (car (gimp-image-get-active-drawable image)) outputFilename outputFilename (/ outputQuality 100) 0 TRUE TRUE "" 2 TRUE 0 (if (null? JPEGDCT) 0 (car JPEGDCT)))
)
)
(else
(let*
(
(outputFilename (string-append outputFilenameNoExtension ".gif")) ;add the new extension
)
(gimp-image-convert-indexed image 1 0 256 TRUE TRUE "")
(file-gif-save RUN-NONINTERACTIVE image (car (gimp-image-get-active-drawable image)) outputFilename outputFilename FALSE FALSE 0 0)
)
)
)
)
(gimp-image-delete image)
)
(if (= fileCount 1) 1 (smart-resize (- fileCount 1) (cdr sourceFiles))) ;determine whether to continue the loop
)
;detect OS type(from http://www.gimp.org/tutorials/AutomatedJpgToXcf/)
(define isLinux
(>
(length (strbreakup sourcePath "/" ) ) ;returns the number of pieces the string is broken into
(length (strbreakup sourcePath "\\" ) )
)
)
(define sourceFilesGlob (file-glob (if isLinux (string-append sourcePath "/*.*") (string-append sourcePath "\\*.*")) 0))
(if (pair? (car (cdr sourceFilesGlob))) ;check for valid source folder(if this script is called from another script they may have passed an invalid path and it's much more helpful to return a meaningful error message)
(smart-resize (car sourceFilesGlob) (car (cdr sourceFilesGlob)))
(error (string-append "Invalid Source Folder " sourcePath))
)
)
;dialog
(script-fu-register
"script-fu-batch-smart-resize" ;function name
"batch-smart-resize" ;menu label
"Crop to layer mask, resize within maximum dimensions, and pad to max dimensions(optional)" ;description
"per1234" ;author
"" ;copyright notice
"2015-10-02" ;date created
"" ;image type
SF-DIRNAME "Source Folder" "" ;sourcePath
SF-DIRNAME "Destination Folder" "" ;destinationPath
SF-STRING "Output Filename Modifier(appended)" "" ;filenameModifier
SF-OPTION "Output Type" '("PNG" "JPEG" "GIF") ;outputType
SF-VALUE "Output Quality(JPEG only) 0-100" "90" ;outputQuality
SF-VALUE "Max Width" "1500" ;maxWidth
SF-VALUE "Max Height" "1500" ;maxHeight
SF-TOGGLE "Pad" TRUE ;pad
SF-COLOR "Padding Color" "white" ;padColor
)
(script-fu-menu-register "script-fu-batch-smart-resize"
"<Image>/Tools") ;menu location
I have tried just about everything I could find online, and this is my last resort. Am I missing syntax that was acceptable on the Windows 7 version, that is not so in the Windows 10 version?
Thanks!

strbreakup is defined in script-fu-compat.init in /usr/share/gimp/2.0/scripts (or C:\Program Files\GIMP 2\share\gimp\2.0\scripts for Windows). Is this file present and complete (372 lines in my working version)?
Edit: summary from comments: Gimp didn't look in its standard scripts directory. The directory above should be listed in Edit>Preferences>Folders>Scripts.

Related

gimp script-fu: drawing a simple line

I'm trying to draw a line using gimp scripting. In the following script: I create a new image 512x512 and a I try to draw a diagonal (0,0)->(512,512).
But the image (tmp.xcf) remains transparent.
What am i doing wrong ?
(define (drawdiagonal W H)
(let* (
(img 0)
(bg 0)
(points (cons-array 4 'double) )
)
(set! img (car (gimp-image-new W H 0)))
(gimp-image-undo-group-start img)
(gimp-context-push)
(set! bg (car (gimp-layer-new img W H RGBA-IMAGE "background" 100 NORMAL-MODE)))
(gimp-image-add-layer img bg 0)
(gimp-drawable-set-visible bg TRUE)
(gimp-image-set-active-layer img bg)
(gimp-context-set-brush-size 10.0)
(gimp-context-set-opacity 100)
(gimp-context-set-paint-mode NORMAL-MODE)
(gimp-context-set-foreground '(255 127 0))
(gimp-selection-all img)
(aset points 0 0)
(aset points 1 0)
(aset points 2 W)
(aset points 3 H)
(gimp-paintbrush-default bg 4 points)
(gimp-context-pop)
(gimp-image-undo-group-end img)
(gimp-xcf-save 1 img img "tmp.xcf" "tmp.xcf")
(display "DONE")
)
)
(drawdiagonal 512 512) (gimp-quit 0)
usage:
cat test.scm | gimp -i -b -
It seems you are mostly missing:
(gimp-context-set-brush "Some brush")
Additionally, the 3rd argument there (2nd img) should be a drawable:
(gimp-xcf-save 1 img img "tmp.xcf" "tmp.xcf")
PS: do yourself a favor and write your script in Python. See here for an example.
Edit post-Gimp 2.10: there is now an API to stroke paths in "Line" mode which is the best way to obtain clean results. The relevant calls are:
pdb.gimp_context_set_stroke_method(STROKE_LINE)
pdb.gimp_context_set_line_cap_style(...)
pdb.gimp_context_set_line_join_style(...)
pdb.gimp_context_set_line_miter_limit(...)
pdb.gimp_context_set_line_width(width)
pdb.gimp_drawable_edit_stroke_item(drawable, path)
For those who may come along later, based on xenoid's hints, I made this using gimp-pencil.
[I found no documentation for the 'item/path' of gimp_drawable_edit_stroke_item]
Could be tweaked to use gimp-brush...
(define (util-draw-path drawable args . path)
;; Draw a zig-zag line:
;; (util-draw-path layer `((width 10) (color ,RED)) 10 50 40 80 70 30 110 90 150 80)
(let ((save (util-assq 'save args) #t)
(color (util-assq 'color args)) ; else use context value
(width (util-assq 'width args)) ; else use context value
(miter (util-assq 'miter args)) ; else use context value
(join (util-assq 'join args)) ; else use context value
(cap (util-assq 'cap args)) ; else use context value
(stroke (util-assq 'stroke args)) ; else use context value
)
(and save (gimp-context-push))
(and miter (gimp-context-set-line-miter-limit miter)) ; default: 10, default mitre up to 60 pixels
(and stroke (gimp-context-set-stroke-method stroke)) ; default STROKE-PAINT-METHOD
(and cap (gimp-context-set-line-cap-style cap)) ; CAP-ROUND, CAP-SQUARE
(and join (gimp-context-set-line-join-style join)) ; JOIN-MITER, JOIN-ROUND, JOIN-BEVEL
(and color (gimp-context-set-foreground color)) ;
(and width (gimp-context-set-line-width width)) ; default: 6
(let ((vec (apply vector path)))
(gimp-pencil drawable (vector-length vec) vec))
(and save (gimp-context-pop))
))
;;; (util-assq key list [default]) -> value or [default or #f]
(macro (util-assq form)
(let ((key (cadr form)) (lis (caddr form))
(els (if (pair? (cdddr form)) (cadddr form) #f)))
`(let ((v (assq ,key ,lis)))
(if v (cadr v) ,els))))

autolisp list used as points in Autocad

I'm new to LISP in autocad. The code shown below draws circles (with radius of 1) in a sloped line. What I don't understand is the value of "a" does not increase in increments of 1. The center of the circle drawn in autocad is (1,1) , (1.7071,1.7071) , (3,3) , (3.7071,3.7071) , (5,5) ... Can someone pls. explain why?
(defun c:wwq ()
(setq a 0)
(while (< a 10)
(setq a (+ 1 a))
(setq pt1 (list a a ) )
(command "circle" pt1 1 )
)
)
While using the AutoLISP command function, you have to care about active object snaps.
One way is to force object snaps to "none" within the (command ...) expression:
(defun c:wwq (/ a pt1)
(setq a 0)
(while (< a 10)
(setq a (+ 1 a))
(setq pt1 (list a a))
(command "_circle" "_none" pt1 1)
)
(princ)
)
Or, you can deactivate every osnap by setting the OSMODE system variable to 0 at the begining of the code and retore the previous value at the end (to be really safe, this method should need and error handler to insure the the previous value is reset in case an error occur during the code execution).
(defun c:wwq (/ a pt1 os)
(setq a 0
os (getvar 'osmode)
)
(setvar 'osmode 0)
(while (< a 10)
(setq a (+ 1 a))
(setq pt1 (list a a))
(command "_circle" pt1 1)
)
(setvar 'osmode os)
(princ)
)
Another way is to use the entmake function which is faster and do not care about osnaps.
(defun c:wwq (/ a)
(setq a 0.0)
(while (< a 10.0)
(setq a (+ 1.0 a))
(entmake
(list
(cons 0 "CIRCLE")
(list 10 a a 0.0)
(cons 40 1.0)
)
)
)
(princ)
)

resize image using script-fu gimp

I'm trying to prepare a script for auto-resizing image files.
I found this LINK but I cannot figure out how to use it.
Anyone can provide a working script that I can use as a starting point?
The following function resizes the image:
(define (resize-image filename-in filename-out new-width new-height)
(let* ((image (car (gimp-file-load RUN-NONINTERACTIVE filename-in "")))
(drawable (car (gimp-image-active-drawable image)))
)
(gimp-image-scale image new-width new-height)
(gimp-file-save RUN-NONINTERACTIVE image drawable filename-out "")
)
)
Now, resizing all jpg's in a directory:
(define (file-basename filename)
(let*
(
(broken-up (strbreakup filename "."))
(wo-last-r (cdr (reverse broken-up)))
(wo-last (reverse wo-last-r))
(result "")
)
(while wo-last
(set! result (string-append result (car wo-last) ))
(set! wo-last (cdr wo-last))
(if (> (length wo-last) 0) (set! result (string-append result ".")))
)
result
)
)
(define (ex_09 file-pattern new-width new-height )
(let* ( (filelist (cadr (file-glob file-pattern 1))))
(while (not (null? filelist))
(let* ( (cur-file (car filelist)) )
(resize-image
cur-file
(string-append (file-basename cur-file) "_resized.jpg")
100
100
)
(set! filelist (cdr filelist))
)
)
)
)
I think that this is your answer.
The code come from this address.
http://www.adp-gmbh.ch/misc/tools/script_fu/ex_09.html
Out of the box it doesn't work for me.
I made some changes :
In the file_basename.scm file I remove some stuff I didn't get around with.
So the resized files are created in the same directory than the original files :
(define (file-basename filename)
(let*
(
(broken-up (strbreakup filename "."))
(wo-last-r (cdr (reverse broken-up)))
(wo-last (reverse wo-last-r))
(car broken-up)
)
)
In the ex_09.scm file :
I just used the new-width and the new-height variables.
(define (ex_09 file-pattern new-width new-height )
(let* ( (filelist (cadr (file-glob file-pattern 1))))
(while (not (null? filelist))
(let* ( (cur-file (car filelist)) )
(resize-image
cur-file
(string-append (file-basename cur-file) "_resized.jpg")
new-width
new-height
)
(set! filelist (cdr filelist))
)
)
)
)
Hop this helps !
and thank yo René Nyffenegger for the code.
:)

Gimp script-fu with illegal function error

I'm working on my first script-fu and scheme is still not very clear for me.
My script works fine but I want to add an other parameter (onlyvisible) and I have a line causing illegal function error in a certain place but not in an other place.
Thank you for your help :-)
Here is my line:
(display " onlyvisible: ")(display onlyvisible)(newline)
Here is my code:
(define (pitibalrog-test img filename onlyvisible)
(let*
(
(imgcopy (car ( gimp-image-duplicate img))) ; Copy to avoid changes on the original image
)
(display " onlyvisible: ")(display onlyvisible)(newline)
(pitibalrog-export-layers imgcopy (gimp-image-get-layers imgcopy) filename onlyvisible)
)
)
(define (pitibalrog-export-layers img listlayers filename onlyvisible)
(let*
(
(nblayers (car listlayers))
(layers (cadr listlayers))
(display "EXPORT LAYERS: LAYERS = ")(display layers)(newline)
(display " onlyvisible: ")(display onlyvisible)(newline) ; <--- HERE IT WORKS
(index 0)
(basename (unbreakupstr (butlast (strbreakup filename ".")) "."))
(extension (car (last (strbreakup filename "."))))
(layer)
)
(display " onlyvisible: ")(display onlyvisible)(newline) ; <--- HERE IS THE PROBLEM
(while (< index nblayers)
(set! layer (aref layers index))
(gimp-item-set-visible layer FALSE)
(set! index (+ index 1))
)
(set! index 0)
(while (< index nblayers)
(set! layer (aref layers index))
(set! filename (string-append basename (car(gimp-drawable-get-name layer)) "." extension))
(pitibalrog-export-layer img layer filename onlyvisible)
(set! index (+ index 1))
)
)
)
(define (pitibalrog-export-layer img layer filename onlyvisible)
(display " - export layer: ")(display layer)(newline)
(gimp-item-set-visible layer TRUE)
; LAYER GROUP
(when (= (car(gimp-item-is-group layer)) 1)
(display "Layer ")(display layer)(display " is a group")(newline)
(pitibalrog-export-layers img (gimp-item-get-children layer) filename onlyvisible)
)
; REAL LAYER
(when (= (car(gimp-item-is-group layer)) 0)
(display "Layer ")(display layer)(display " is not a group")(newline)
; (gimp-file-save RUN-NONINTERACTIVE img layer filename filename) ; NO MASK HANDLING!!!
(gimp-file-save RUN-WITH-LAST-VALS img layer filename filename)
)
(gimp-item-set-visible layer FALSE)
)
(script-fu-register "pitibalrog-test"
"<Image>/Script-Fu/Utils/pitibalrog-test..."
"Export all layers of the image in separete files" ;comment
"pitiBalrog" ;author
"pitiBalrog" ;copyright
"November 2012" ;date
"*A"
SF-IMAGE "img" 0
SF-FILENAME "destination" ""
SF-TOGGLE "Export only visible layers" TRUE
)
Disclaimer: I have never done any work with script-fu, so I have no idea what those script-fu specific procedures do. Scheme, however, I can do.
Please look closely at the syntax required for the let special form:
(let <List of forms that assign values>
<body>)
I think your main problem comes from the fact that in scheme you are allowed to change the value of almost anything -- there are very few reserved words like other languages. So, when you say (let ((display 3)) <body>), display no longer points to the procedure that displays things to the REPL. Then, in the body of your let* when you say (display " onlyvisible") you're trying to call something as a function that is not a function -- in this case whatever the value of layers is.
In general, all code that needs to do something like display should be in body of the function. For example:
(let ((foo 3) ; associate symbol foo with the value 3
(bar "I'm a string!") ; associate symbol bar with a string
(* '(a b c))) ; associate symbol * with a list '(a b c)
(display foo) ;\
(newline) ; \
(display bar)) ; }-- expressions that make up the body
(newline) ; /
(display *) ; /
(* 3 4)) ;/ --- this is the same type of error you made
;;Output
3
I'm a string!
(a b c)
ERROR -- invalid function
Finally, please do not format scheme code as you would C or Java, etc. Here is the schemer-friendly version of your first procedure:
(define (pitibalrog-test img filename onlyvisible)
(let ((img copy (car (gimp-image-duplicate img))))
(display " onlyvisible: ")
(display onlyvisible)
(newline)
(pitibalrog-export-layers imgcopy (gimp-image-get-layers imgcopy) filename onlyvisible)))
Well formatted code makes schemers happy and you are more likely to receive speedy help.

How to parse out base file name using Script-Fu

Using Gimp 2.6.6 for MAC OS X (under X11) as downloaded from gimp.org.
I'm trying to automate a boring manual process with Script-Fu. I needed to parse the image file name to save off various layers as new files using a suffix on the original file name.
My original attempts went like this but failed because (string-search ...) doesn't seem to be available under 2.6 (a change to the scripting engine?).
(set! basefilename (substring filename 0 (string-search "." filename)))
Then I tried to use this information to parse out the base file name using regex but (re-match-nth ...) is not recognized either.
(if (re-match "^(.*)[.]([^.]+)$" filename buffer)
(set! basefilename (re-match-nth orig-name buffer 1))
)
And while pulling the value out of the vector ran without error, the resulting value is not considered a string when it is passed into (string-append ...).
(if (re-match "^(.*)[.]([^.]+)$" filename buffer)
(set! basefilename (vector-ref buffer 1))
)
So I guess my question is, how would I parse out the base file name?
Not really a correct solution:
> (filename-basename "this.is.a.long.filename.jpg")
"this"
A better implementation:
(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)
)
)
)
Context
GIMP 2.6.6 Windows Vista SP2
Goal
Extract the basename of the original filename without its extension.
Symptom
Error: eval: unbound variable:
re-match-nth
Possible suggestion
GIMP menu "Filters" > "Script-Fu" > "Console"
In the input box, paste the following Script-Fu definition of function
then hit the ENTER key:
(define (filename-basename orig-name)
(car (strbreakup orig-name "."))
; Nimmzo 09/09/30: the string split function strbreakup is defined
; in the compatibility file from SIOD to TinyScheme:
; C:\Program Files\GIMP\share\gimp\2.0\scripts\script-fu-compat.init
) ; end filename-basename
To test the function, enter:
(filename-basename "screen.xcf")
The Script-Fu Console answers:
"screen"
My version splits the filename (f) into parts delimited by separator ("." in this case); drops last part; and re-combines them with separator again
(define (pc-drop-extension f)
(unbreakupstr (butlast (strbreakup f ".")) ".") )
so
(pc-drop-extension "ab.cd.efi") -> "ab.cd"
and
(pc-drop-extension "ab/cd.ef/ghi.jkl.mno") -> "ab/cd.ef/ghi.jkl"
Many thanks philcolbourn for pointing out a "simple" way to do this.
Unfortunately, the butlast function is deprecated:
http://www.gimp.org/docs/script-fu-update.html#deprecated
Here is philcolbourn's version with the suggested replacement:
(define (drop-extension filename)
(unbreakupstr (reverse (cdr (reverse (strbreakup filename ".")))) ".")
)
As in Gimp 2.8 "gimp-image-get-uri" has to be used to get the filename of a JPG file, but gimp-image-get-uri delivers the complete path, I used this function to extract just the name of the pic (without the suffix ".jpg"):
(let* (
(uriname (car (gimp-image-get-uri IMAGE)))
(basename (car (reverse (strbreakup (car (strbreakup uriname ".")) "/"))))
...
)
...
)
For those looking for a true string-replace functionality, here is a function I wrote for use in Script Fu
(define (string-replace strIn strReplace strReplaceWith)
(let*
(
(curIndex 0)
(replaceLen (string-length strReplace))
(replaceWithLen (string-length strReplaceWith))
(inLen (string-length strIn))
(result strIn)
)
;loop through the main string searching for the substring
(while (<= (+ curIndex replaceLen) inLen)
;check to see if the substring is a match
(if (substring-equal? strReplace result curIndex (+ curIndex replaceLen))
(begin
;create the result string
(set! result (string-append (substring result 0 curIndex) strReplaceWith (substring result (+ curIndex replaceLen) inLen)))
;now set the current index to the end of the replacement. it will get incremented below so take 1 away so we don't miss anything
(set! curIndex (-(+ curIndex replaceWithLen) 1))
;set new length for inLen so we can accurately grab what we need
(set! inLen (string-length result))
)
)
(set! curIndex (+ curIndex 1))
)
(string-append result "")
)
)

Resources