; 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.
(define (script-fu-sg-viktoria orig-image orig-drawable)
(define v-grid '( 65 140 215 265 315 365 415 465 515 565 615
665 715 765 815 865 915 965 1015 1065 1115 1165 1215 ))
(define h-grid '(210 245 280 315 350 385 420 455 490 525 560
595 630 665 700 735 770 805 840 875 910 945 ))
; Splits a list at k'th element
; Returns a pair of lists ((prefix) . (suffix))
(define (split lis k)
(let loop ((suffix lis)
(prefix '())
(k (min k (length lis))) )
(if (zero? k)
(cons (reverse prefix) suffix)
(loop (cdr suffix)
(cons (car suffix) prefix)
(pred k) ))))
(define (row-solid? image drawable y)
(let ((color (vector->list (cadr (gimp-drawable-get-pixel drawable 0 y))))
(width (car (gimp-image-width image))) )
(gimp-rect-select image 0 y width 1 CHANNEL-OP-REPLACE 0 0)
(gimp-by-color-select drawable
color
1
CHANNEL-OP-SUBTRACT
FALSE
FALSE
0
0 ) )
(not (zero? (car (gimp-selection-is-empty image)))) )
(define (col-solid? image drawable x)
(let ((color (vector->list (cadr (gimp-drawable-get-pixel drawable x 0))))
(height (car (gimp-image-height image))) )
(gimp-rect-select image x 0 1 height CHANNEL-OP-REPLACE 0 0)
(gimp-by-color-select drawable
color
1
CHANNEL-OP-SUBTRACT
FALSE
FALSE
0
0 ))
(not (zero? (car (gimp-selection-is-empty image)))) )
(define (place-text layer text x-list y-list)
(let ((image (car (gimp-drawable-get-image layer)))
(text-width (car (gimp-text-get-extents-fontname text
20
PIXELS
"Sans Bold" ))) )
(gimp-floating-sel-anchor
(car (gimp-text-fontname image
layer
(+ (car x-list)
(/ (- (cadr x-list)
(car x-list) )
2 )
(- (/ text-width 2)) )
(+ (car y-list) 6)
text
0
TRUE
20
PIXELS
"Sans Bold" )))))
(define (create-table-image)
(let* ((table-image (car (gimp-image-new 1280 1024 RGB)))
(table-layer (car (gimp-layer-new table-image 1280 1024 RGB-IMAGE "Table" 100 NORMAL-MODE)))
(buffer (car (gimp-edit-named-copy orig-drawable "BG")))
(thumbnail-layer 0)
(brush (car (gimp-brush-new "temp-brush"))) )
(gimp-image-undo-disable table-image)
(gimp-image-add-layer table-image table-layer 0)
(gimp-drawable-fill table-layer WHITE-FILL)
(set! thumbnail-layer (car (gimp-edit-named-paste table-layer buffer TRUE)))
(gimp-buffer-delete buffer)
; insert thumbnail image in upper right corner
(let* ((layer-width (car (gimp-drawable-width thumbnail-layer)))
(layer-height (car (gimp-drawable-height thumbnail-layer)))
(aspect (/ layer-height layer-width)) )
(if (< (/ 300 layer-width) (/ 190 layer-height))
(begin
(gimp-layer-scale-full thumbnail-layer
300
(* 300 aspect)
TRUE
INTERPOLATION-LANCZOS)
(gimp-layer-set-offsets thumbnail-layer
(- (car (last v-grid)) 300)
(+ 10 (/ (- 190 (* 300 aspect)) 2)) ))
(begin
(gimp-layer-scale-full thumbnail-layer
(/ 190 aspect)
190
TRUE
INTERPOLATION-LANCZOS)
(gimp-layer-set-offsets thumbnail-layer
(- (car (last v-grid)) (/ 190 aspect))
10 ))))
(gimp-floating-sel-anchor thumbnail-layer)
(gimp-context-push)
(gimp-context-set-foreground '(0 0 0))
(gimp-brush-set-radius brush 1)
(gimp-context-set-paint-method "gimp-paintbrush")
(gimp-context-set-brush brush)
(gimp-context-set-paint-mode NORMAL-MODE)
(gimp-progress-pulse)
(gimp-progress-set-text "Creating page...")
(let loop ((v-lines v-grid)) ; paint horizontal lines
(gimp-progress-pulse)
(unless (null? v-lines)
(gimp-paintbrush table-layer 0 4
(vector (car v-lines) (car h-grid) (car v-lines) (car (last h-grid)))
PAINT-CONSTANT
0 )
(loop (cdr v-lines)) ))
(let loop ((h-lines h-grid)) ; paint vertical lines
(gimp-progress-pulse)
(unless (null? h-lines)
(gimp-paintbrush table-layer 0 4
(vector (car v-grid) (car h-lines) (car (last v-grid)) (car h-lines))
PAINT-CONSTANT
0 )
(loop (cdr h-lines)) ))
(let loop-cols ((xs (cdddr v-grid))) ; select grayed cells
(gimp-progress-pulse)
(unless (null? xs)
(let loop-rows ((ys (butlast h-grid)) )
(if (null? ys)
(loop-cols (cddr xs))
(begin
(gimp-image-select-contiguous-color table-image
CHANNEL-OP-ADD
table-layer
(+ (car xs) 2)
(+ (car ys) 2) )
(loop-rows (cdr ys)) )))))
(gimp-context-set-foreground '(228 228 228))
(gimp-edit-fill table-layer FOREGROUND-FILL)
(gimp-selection-none table-image)
(gimp-brush-delete brush)
(gimp-context-set-foreground '(0 0 0))
(place-text table-layer "ROW" v-grid h-grid)
(place-text table-layer "READ" (cdr v-grid) h-grid)
(let loop ((xs (cddr v-grid))
(text? #f) )
(gimp-progress-pulse)
(unless (null? (cdr xs))
(place-text table-layer (if text? "S" "O") xs h-grid)
(loop (cdr xs)
(not text?) )))
(gimp-context-pop)
table-image
)
)
; MAIN processing starts here
(gimp-context-push)
(let* ((width (car (gimp-drawable-width orig-drawable)))
(height (car (gimp-drawable-height orig-drawable)))
(buffer (car (gimp-edit-named-copy orig-drawable "BG")))
(image (car (gimp-edit-named-paste-as-new buffer)))
(layer (car (gimp-image-flatten image))) )
(gimp-buffer-delete buffer)
(unless (zero? (car (gimp-image-base-type image)))
(gimp-image-convert-rgb image) )
(gimp-image-convert-indexed image
NO-DITHER
MAKE-PALETTE
3
FALSE
FALSE
"")
(gimp-image-convert-rgb image)
(plug-in-autocrop RUN-NONINTERACTIVE image layer)
(set! width (car (gimp-image-width image)))
(set! height (car (gimp-image-height image)))
(gimp-progress-pulse)
(gimp-progress-set-text "Scanning...")
(let ((rows (let loop ((y 0)
(rows '()) )
(gimp-progress-pulse)
(while (and (< y height) (row-solid? image layer y) )
(set! y (succ y)) )
(let ((start-row y))
(while (and (< y height) (not (row-solid? image layer y)) )
(set! y (succ y)) )
(if (= start-row y)
(reverse rows)
(loop (succ y)
(cons (cons start-row y) rows) ))))))
(let ((cols (let loop ((x 0)
(cols '()) )
(gimp-progress-pulse)
(while (and (< x width) (col-solid? image layer x) )
(set! x (succ x)) )
(let ((start-col x))
(while (and (< x width) (not (col-solid? image layer x)) )
(set! x (succ x)) )
(if (= start-col x)
(reverse cols)
(loop (succ x)
(cons (cons start-col x) cols) ))))))
(set! rows (map (lambda (x) (/ (+ (car x) (cdr x)) 2)) rows))
(set! cols (map (lambda (x) (/ (+ (car x) (cdr x)) 2)) cols))
(gimp-selection-none image)
(gimp-threshold layer 127 255)
(gimp-image-convert-grayscale image)
(set! rows
(let loop-rows ((rows rows)
(row-result '()) )
(if (null? rows)
(reverse row-result)
(let ((row (map (lambda (x)
(zero? (vector-ref (cadr (gimp-drawable-get-pixel layer x (car rows))) 0)) )
cols) ))
(let rle ((cols row)
(result '()) )
(let ((rest (member (not (car cols)) cols)))
(if rest
(rle rest
(cons (cons (car cols) (- (length cols) (length rest))) result) )
(loop-rows (cdr rows)
(cons (cons (cons (car cols) (length cols)) result) row-result) ))))))))
; Our tables need to be filled from bottom of the scanned image to its top
(set! rows (reverse rows))
; Now we need to reverse direction for every other row
(set! rows
(let loop ((rows rows)
(result '())
(rvs? #f) )
(if (null? rows)
(reverse result)
(loop (cdr rows)
(cons (if rvs? (reverse (car rows))
(car rows) )
result )
(not rvs?) ))))
; At this point we have a list of row information, ready to create our table
(gimp-image-delete image)
; Next we need to ensure that each row starts with an OPEN run (possibly of zero length)
(set! rows
(let loop ((rows rows)
(result '()) )
(if (null? rows)
(reverse result)
(if (caaar rows)
(loop (cdr rows)
(cons (cons 0 (map cdr (car rows))) result) )
(loop (cdr rows)
(cons (map cdr (car rows)) result) )))))
; Next we prepend a row number to each row and insure each line
; has no more than 20 cells.
(set! rows
(let loop ((rows rows)
(row-num 1)
(continued? #f)
(result '()) )
(if (pair? rows)
(let ((remaining (cdr (split (car rows) 20)))
(this-row (car (split (car rows) 20))) )
(if (pair? remaining)
(loop (cons remaining (cdr rows))
row-num
#t
(append result (list (cons (if continued?
"---"
(number->string row-num) )
this-row))) )
(loop (cdr rows)
(succ row-num)
#f
(append result (list (cons (if continued?
"---"
(number->string row-num) )
this-row))) )))
result )))
(gimp-context-set-foreground '(0 0 0))
(let ((h-grid (cdr h-grid)))
(let ((images
(let page-loop ((page-rows (car (split rows 20)))
(remaining-rows (cdr (split rows 20)))
(page-num 1)
(row-grid h-grid)
(tables '()) )
(if (null? page-rows)
(if (null? remaining-rows)
tables ; Done!
(page-loop (car (split remaining-rows 20))
(cdr (split remaining-rows 20))
(succ page-num)
h-grid
tables ))
(begin
(let* ((table-image (create-table-image))
(table-layer (car (gimp-image-get-active-layer table-image))) )
(gimp-progress-pulse)
(gimp-progress-set-text "Filling table...")
(set! tables (cons table-image tables))
(let line-loop ((line-rows page-rows)
(row-offsets h-grid)
(line-cnt (length page-rows)) )
(unless (or (null? line-rows) (zero? line-cnt))
(place-text table-layer (caar line-rows) v-grid row-offsets)
(let ((num-text (caar line-rows)))
(place-text table-layer num-text v-grid row-offsets)
(place-text table-layer
(if (string=? num-text "---")
"-"
(if (even? (string->number num-text))
"L"
"R" ))
(cdr v-grid)
row-offsets ))
(let cell-loop ((cells (cdar line-rows))
(col-grid (cddr v-grid)) ; skip the 'row' and 'read' columns
(cell-cnt 20) )
(gimp-progress-pulse)
(unless (or (zero? cell-cnt)
(null? cells) )
(place-text table-layer (number->string (car cells)) col-grid row-offsets)
(cell-loop (cdr cells)
(cdr col-grid)
(pred cell-cnt) )))
(line-loop (cdr line-rows)
(cdr row-offsets)
(pred line-cnt) ))))
(page-loop '()
remaining-rows
(succ page-num)
h-grid
tables ))))))
(let loop ((images images))
(unless (null? images)
(gimp-image-undo-enable (car images))
(gimp-display-new (car images))
(loop (cdr images)) ))
))))
(gimp-displays-flush)
(gimp-progress-end)
)
(gimp-context-pop)
)
(script-fu-register "script-fu-sg-viktoria"
"_Crochet Pattern by Viktoria"
"Create crochet instructions per Viktoria"
"Saul Goode"
"Saul Goode"
"April 2013"
"*"
SF-IMAGE "Image" 0
SF-DRAWABLE "Drawable" 0
)
(script-fu-menu-register "script-fu-sg-viktoria"
"<Image>/File/Create"
)