Batch Convert Image Files with Racket

With Racket's path utilities I was able to get this script working a lot faster than I was able to get an earlier Guile script working. (fn:1) Why are command-line arguments treated as vectors? That's a mystery for later. In the meantime this script is useful, and it is the first step toward getting all my teaching materials generated through Racket scripts. DrRacket on windows may provide a way to ease teachers towards independence with free software. Once people can do everything with free applications, like DrRacket, Gimp, Firefox, and Inkscape: they won't feel dependent on a proprietary OS.

DrRacket Screenshot: jpgs->svgs

Maybe it's just because of all the hours spent with the Racket Language since I made the Guile Scheme script, but Racket seemed to let me develop a batch conversion script much faster. Previous experience with `build-path` helped a lot, I never saw anything similar in the Guile documentation so a lot of time went into developing string-based utilities to manipulat paths. I imagine this Racket version could work in DrRacket on a Windows OS, if ImageMagick's `convert` and the `potrace` utility are installed.

Emacs script and shell buffers

code ~/.rkt-jpgs->svgs.rkt

    #! /usr/bin/env racket
    #lang racket
    ;; Convention: .jpg image files must be in directoy "jpgs"
    
    ;; for use as GNU/Linux script, delete line for use in DrRacket on Windows (might work??)
    (define dir (vector-ref (current-command-line-arguments) 0))
    
    ;; In place of the above line, the following line might work in DrRacket on Windows
    #; (define working-directory (build-path (find-system-path 'home-dir)
                                          "Racket" "BatchConvert" "imgs" ))
    
    ;; for use as GNU/Linux script, delete or comment out the following line for use in DrRacket.
    (define working-directory (build-path (current-directory-for-user) dir))
    
    
    (define jpg-dir-path (build-path working-directory "jpgs"))
    (define svg-dir-path (build-path working-directory "svgs"))
    (make-directory* svg-dir-path)
    
    ;; /doc/racket/style/Choosing_the_Right_Construct.html
    ;;  Lambda vs. Define
    
    (define jpg-files (directory-list jpg-dir-path))
    (define jpg-paths
      (map (lambda (p) (path->complete-path p jpg-dir-path))
                   (filter (lambda (p)
                             (or (path-has-extension? p #".jpg")
                                 (path-has-extension? p #".JPG")
                                 (path-has-extension? p #".jpeg")))
                           jpg-files)))
    
    (define svg-paths
      (map (lambda (p) (path->complete-path p svg-dir-path))
           (map (lambda (p) (path-replace-extension p ".svg"))
                jpg-files)))
    
    (define jpg2svg
      (lambda (jpg-pth svg-pth)
        "convert .jpg file to .svg file using system calls to ImageMagick's `convert' and  then to `potrace'."
        (let* ((pnm-pth (path-replace-extension jpg-pth ".pnm"))
               (pnm (path->string pnm-pth))
               (svg (path->string svg-pth))
               (jpg (path->string jpg-pth))
               (convert (find-executable-path "convert"))
               (potrace (find-executable-path "potrace"))
               (rm (find-executable-path "rm")))
          (system* convert jpg pnm)
          (system* potrace "-b" "svg" "--tight" "-M" ".02" "--opaque" pnm "-o" svg)
          (system* rm pnm))))
    
    (for-each jpg2svg jpg-paths svg-paths)

Example png images converted from svg files To share line drawings on-line, this script takes the svg files (which are much cleaner than .jpg photos no matter how painstakingly edited) an converts them to 250 pixel tall .png files with the craftsperson-teacher's name in the bottom-left corner. The aim is for a subtle watermark. I hope to share code and lesson plans (sequences of situations for learning language) with GNU licenses, but the line drawings are Mio Sato's. She is sure what sort of license she wants, so for now the pictures are “all rights reserved.”

DrRacket Screenshot: svg->pngs with watermark

code ~/.rkt-svgs->pngs.rkt

    #! /usr/bin/env racket
    #lang racket
    
    ;; for use as GNU/Linux script 
    (define dir (vector-ref (current-command-line-arguments) 0))
    
    ;; for use in DrRacket on Windows replacing the above line with this one might work
    #; (define working-directory (build-path (find-system-path 'home-dir)
                                          "Racket" "BatchConvert" "imgs" ))
    
    ;; for use as GNU/Linux script, delete the following line in DrRacket on Windows
    (define working-directory (build-path (current-directory-for-user) dir))
    
    (define svg-dir-path (build-path working-directory "svgs"))
    (define png-dir-path (build-path working-directory "pngs"))
    
    (make-directory* png-dir-path)
    
    (define svg-files (filter (lambda (p)
                                (path-has-extension? p #".svg"))
                              (directory-list svg-dir-path)))
    (define svg-paths
      (map (lambda (p)
             (path->complete-path p svg-dir-path))
           svg-files))
    
    (define png-paths
      (map (lambda (p) (path->complete-path p png-dir-path))
           (map (lambda (p) (path-replace-extension p ".png"))
                svg-files)))
    
    (define svg2png
      (lambda (svg-pth png-pth)
        (let* ((svg (path->string svg-pth))
               (png (path->string png-pth))
               (convert (find-executable-path "convert")))
          (system* convert svg "-background" "white" "-layers" "merge" "-geometry" "x250" "-bordercolor" "white" "-gravity" "southwest" "-pointsize" "10" "-fill" "grey" "-annotate" "+0+0" "MioSato" png))))
    
    (for-each svg2png svg-paths png-paths)

#BatchConvertImages #Racket #LearningRacket #ScriptingRacket #Scheme #jpgtosvg #svgtopng #BatchConversion #DrRacket