Mail-merge in Gimp
I need to frequently use mail-merge in Gimp. I finally found a good script, thanks to Saul Goode.
Here is the script.
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation; either version 2 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
; This script reads in a data file containing a list of
; team members' names and associated information. For each person listed
; in the data file, an XCF file is produced incorporating the data
; into a master template image.
; The data file uses Lisp/Scheme style lists and commenting (comments
; start with a semi-colon and extend to the end of the line).
; The first non-comment line of the data file should be a list containing
; the field names of the player information wrapped in parentheses.
; For example:
;
; ; THIS IS A COMMENT AND IGNORED
; ( athlete-name club-name athlete-number uniform-color )
;
; This "header" list should be followed by the lists containing the
; actual data to be substituted into the template file. The data should
; be text strings and appear in the same order as the fields specified in
; the header list.
;
; ("Kyle" "Manchester United" "21" "red")
; ("Stan" "Arsenal" "15" "blue")
; ("Kenny" "Chelsea" "4" "red")
; ("Token" "Liverpool" "11" "black")
;
;
; The currently open image is used as a template with the upper layers having
; names that match the field names. These field layers are used to determine
; the location and size of the text (this layer is otherwise ignored).
; The name text is centered within the bounds of this field template layer.
;
;
(define (script-fu-sg-goobie image
datafilename
save-xcf
save-jpeg
save-png
save-dir
vert-justification
size-handling)
;; Perform a search for the largest font that will fit within
;; the cell.
;
(define (calc-fontsize text font width height)
(let loop ((fontsize 6) ;; minimum possible fontsize
(last-extents nil)
(last-fontsize 3)
(adjust 2)
)
(let ((extents (gimp-text-get-extents-fontname text fontsize PIXELS font)))
(if (or (= last-fontsize fontsize) (equal? extents last-extents))
(max fontsize 6)
(if (or (> (car extents) width) (> (cadr extents) height))
(loop (truncate (* last-fontsize (+ (* (- adjust 1) 0.5) 1)))
last-extents
last-fontsize
(+ (* (- adjust 1) 0.5) 1) )
(loop (truncate (* fontsize adjust))
extents
fontsize
adjust ))))))
(define (create-data-layer image field-name field-data)
(let ((frame-layer
(let loop ((layers (vector->list (cadr (gimp-image-get-layers image)))))
(if (null? layers)
#f
(if (string=? field-name (car (gimp-drawable-get-name (car layers))))
(car layers)
(loop (cdr layers)) )))))
(if frame-layer
(if (= (car (gimp-drawable-is-text-layer frame-layer)) 1)
(let ((x (car (gimp-drawable-offsets frame-layer)))
(y (cadr (gimp-drawable-offsets frame-layer)))
(w (car (gimp-drawable-width frame-layer)))
(h (car (gimp-drawable-height frame-layer)))
(font (car (gimp-text-layer-get-font frame-layer)))
(size (car (gimp-text-layer-get-font-size frame-layer)))
)
(gimp-image-set-active-layer image frame-layer)
(gimp-text-layer-set-text frame-layer field-data)
(let ((extents (gimp-text-get-extents-fontname field-data
size
PIXELS
font )))
(when (and (zero? size-handling)
(or (> (car extents) w)
(> (cadr extents) h) ))
(gimp-text-layer-set-font-size frame-layer (calc-fontsize field-data font w h) PIXELS) )
(case vert-justification
((0) ; center
(gimp-layer-set-offsets frame-layer
x
(+ y (/ (- h (cadr extents)) 2)) ))
((1) ; top
(gimp-layer-set-offsets frame-layer
x
y ))
((2) ; bottom
(gimp-layer-set-offsets frame-layer
x
(- (+ y h) (cadr extents)) )))))
(gimp-message "Field layer is not a text layer")
)
(begin
(gimp-message (string-append "Field layer not found: " field-name)) ))
frame-layer ))
;; ----------------------------------------------------------------------
;; Main processing start here
;
(let* ((inport (open-input-file datafilename))
(field-names (map symbol->string (read inport)))
(filetag (car field-names)) )
(gimp-image-undo-freeze image)
(gimp-context-push)
(let entry-loop ((fields (read inport)))
(unless (eof-object? fields)
(let ((temp-image (car (gimp-image-duplicate image)))
(filename #f) )
(let field-loop ((field-names field-names)
(field-values fields) )
(unless (null? field-values)
(when (string=? (car field-names) filetag)
(set! filename (car field-values)) )
(create-data-layer temp-image (car field-names) (car field-values))
(field-loop (cdr field-names) (cdr field-values)) ))
(if filename
(let ((fullname (string-append save-dir
DIR-SEPARATOR
filename )))
(unless (zero? save-xcf)
(let ((filename (string-append fullname ".xcf")))
(gimp-xcf-save TRUE
temp-image
(car (gimp-image-get-active-layer image))
filename
filename )))
(unless (zero? save-png)
(let ((layer (car (gimp-image-merge-visible-layers temp-image
CLIP-TO-IMAGE )))
(filename (string-append fullname ".png")) )
(file-png-save2 RUN-NONINTERACTIVE
temp-image
layer
filename
filename
FALSE ; interlace
9
FALSE ; bkgd
(car (gimp-drawable-has-alpha layer))
FALSE ; offs
FALSE ; phys
FALSE ; time
TRUE ; comment
FALSE ; svtrans
)))
(unless (zero? save-jpeg)
(let ((layer (car (gimp-image-flatten temp-image)))
(filename (string-append fullname ".jpg")) )
(file-jpeg-save RUN-NONINTERACTIVE
temp-image
layer
filename
filename
0.93
0 ; smoothing
1 ; optimize
1 ; progressive
"" ; comment
0 ; subsmp (0-4)
1 ; baseline
0 ; restart
0 ;dct
))))
(gimp-message "Error encountered") )
; (gimp-image-delete temp-image)
)
(entry-loop (read inport)) ))
(close-input-port inport)
(gimp-context-pop)
(gimp-image-undo-thaw image)
)
)
(script-fu-register "script-fu-sg-goobie"
"Goobie (uniforms)..."
"Create uniform files based on template image"
"Saul Goode"
"Saul Goode"
"June 2012"
"*"
SF-IMAGE "Image" 0
SF-FILENAME "Data file" "example.data"
SF-TOGGLE "Save as XCF" FALSE
SF-TOGGLE "Save as JPEG" TRUE
SF-TOGGLE "Save as PNG" FALSE
SF-DIRNAME "Save directory" ""
SF-OPTION "Vertical justification" '("Center" "Top" "Bottom")
SF-OPTION "Font sizing (if too large)" '("Fit" "Crop" "Overflow")
)
(script-fu-menu-register "script-fu-sg-goobie"
"
)