webページを作るのにあると便利なので、画像ファイルの幅(width)と高さ(height)を取得するプログラムをSchemeで書いてみた。対応している画像フォーマットは、JPEG、GIF、PNGの3種。使用した処理系はGaucheで、モジュールとして再利用できるようにしている。以下そのソース。
;;; ;;; image-size.scm - For getting image size ;;; ;;; Copyright (c) 2009 Tetsurou, All Rights Reserved. ;;; ;;; version: 1.0 (21-09-17) ;;; (define-module tetsurou.image-size (use binary.pack) (export get-image-width&height get-image-width get-image-height) ) (select-module tetsurou.image-size) ;; getting size of image (define (get-image-width&height file-name) (if (file-exists? file-name) (let ((type ((#/\.(.+?)$/ (sys-basename file-name)) 1))) (call-with-input-file file-name (lambda (p) (if p (case (string->symbol type) ((gif) (get-gif-image-size p)) ((png) (get-png-image-size p)) ((jpeg jpg) (get-jpg-image-size p)) (else (error "Image file is not supported: " (string->symbol type)))) (error "Image file read error!"))) :if-does-not-exist #f)) (error "Image file is not exist."))) ;; getting width of image (define (get-image-width file-name) (receive (w h) (get-image-width&height file-name) w)) ;; getting height of image (define (get-image-height file-name) (receive (w h) (get-image-width&height file-name) h)) ;; getting size of gif image (define (get-gif-image-size port) (if (string=? (string-incomplete->complete (read-block 3 port) :omit) "GIF") (apply values (unpack "x3v2" :input port)) (error "This file is not formated by GIF!"))) ;; getting size of png image (define (get-png-image-size port) (if (equal? (read-block 8 port) #*"\x89PNG\r\n\x1a\n") (apply values (unpack "x8N2" :input port)) (error "This file is not formated by PNG!"))) ;; getting size of jpeg image (define (get-jpg-image-size port) (if (eq? (read-byte port) 255) (case (read-byte port) ((192 194) (apply values (reverse (unpack "x3n2" :input port)))) ((216 217) (get-jpg-image-size port)) ((0) (error "Analysis failed.")) (else (port-seek port (- (car (unpack "n" :input port)) 2) SEEK_CUR) (get-jpg-image-size port))) (error "This file is not formated by JPEG!"))) (provide "tetsurou/image-size")
画像ファイルからのサイズ取得方法は「画像ファイルの幅と高さを取得 - メモ@wantora」を参考にした。Rubyで書かれているのを書き直す際、Schemeっぽく再帰を使ってみる。結構すっきりしたんじゃないだろうか。binaryなファイルをいじるのははじめてだけど、unpuckモジュールを使えば比較的楽にできた。ただ、不完全文字列には若干手こずったけど…。
サイズ取得はフォーマット毎に関数をわけ、それぞれget-XXX-image-sizeとしている。これらの関数は、入力ポートを唯一の引数に取り、取得した画像の幅と高さを多値で返す。エラー処理は適当。
GIFとPNGは決まった位置にサイズの情報があるので比較的簡単。正しいフォーマットかチェックして、サイズ情報が書き込まれている位置を読めばOK。一方JPEGはそうゆう訳にはいかず、マーカコード(marker code)と呼ばれる識別子で情報が区切られているため、まずこれを見つけてやる必要がある。画像のサイズに関係のないマーカは不要なので、マーカの長さを取得しport-seekで読み飛ばしている。またJPEGの画像サイズは、高さ、幅の順で書き込まれているため、reverseをかけて他のフォーマットに合わせた。
フォーマットの振り分けは簡単に拡張子で判断している。実用上は問題ないだろう。モジュール外から利用できる関数は、get-image-width&height、get-image-width、get-image-heightの3つを用意した。
あとは以下のようなスクリプトを適当に書いてやって、シェルから呼び出せば画像の大きさが取得できる。
#!/usr/local/bin/gosh (use tetsurou.image-size) (define (usage) (format (current-error-port) "Usage: ~a file-name\n" *program-name*) (exit 2)) (define (main args) (if (= (length args) 2) (receive (width height) (get-image-width&height (cadr args)) (print "Width: " width ", Height: " height)) (usage)) 0)