#!/usr/bin/env ruby
#
#= 「dcmodel お絵描きサムネイル HTML 作成スクリプト」の作成スクリプト
#
#  Developers :: Yukiko Yamada, Yasuhiro Morikawa
#  Version    :: 2005/04/19 09:19:41
#
#== Overview
#
#実験結果 (主に dcmodel のモデル群によって得られた結果を描画したもの)
#を手っ取り早くサムネイル化して表示するためのしまうためのクラスライブラリ
#である。
#
#このクラスを用いたサンプルスクリプトを出力するメソッドももっている。
#
#
#== Usage
#
#
#とりあえず、このスクリプト本体を実行してほしい。
#
#        $ ruby dcmodel-thum.rb
#
#すると、サンプルとなる Ruby スクリプトが出力されるはずである。
#後は、環境に応じて適宜そのスクリプトの内部を書き換え、
#今度はそのサンプルスクリプトを実行すること。
#出力されるファイルなど、詳しいことは下記のメソッド等の説明を参照のこと。
#
#なお、生成された Ruby スクリプトは本スクリプトに依存しており、
#生成される際に絶対パスで本スクリプトを指定するようになっている。
#本スクリプトの移動や他のホストでの利用の際には注意すること。
#
#== Future Plans
#
#* スクリプト出力用のメソッド DCModelThumbnail.create_sample_rb の改良
#  * サンプルを出力する際に重要なこと。
#    * cvs によるバージョンを出力しておくこと。
#    * ドキュメントの位置・サンプルの位置を記すこと
#    * サンプルの本文には、infofile の書式を記載しておくこと。
#      上記のサンプルの位置を手繰ってこれるとなお良い。
#* infofile において、<ファイル名>:<修飾子>:<コメント> の修飾子で、
#  複数行を許可するものを追加する。
#  * "m" をつけた場合、コメントは "{" と "}" で囲む。
#    * そのため、コメントの最初の文字が "{" でない場合、そのコメントは
#      無視され、次の行は普通に読まれる。
#  * 取得用の DCModelThumbnail.info_opt_parse メソッドをつくり、
#    ハッシュで情報を返すようにすると良いだろう。
#    * 重複する情報については Warning かエラーを出すようにすると良いだろう。
#  * よって、以下の案は破棄予定
#    * 情報ファイルにおいて、バックスラッシュが行末にある場合は継続行
#      として取得できるようにする。その際、次の行の行頭の空白やタブは
#      無視すること。
#* rttool を用いた DCModelThumbnail.rt2html を作成する。
#  * DCModelThumbnail.rd2html と同様にしてやってみる。
#* DCModelThumbnail.rd2html において、現在 rd2html-ext-lib の使用が
#  必須になっているが、ライブラリが存在するかサーチする必要があるだろう。
#  サーチして存在しない場合は、Warning を発して rd2html-lib を用いるか、
#  エラーを吐くようにする。
#
#== Notes
#
#今のところ、特になし。
#
#== Acknowledgements
#
#本プログラムは、
#dcphoto.pl <http://www.ep.sci.hokudai.ac.jp/~totera/official/program/dcphoto/>
#Ver 1.02 - 写真用 HTML作成スクリプト (Mitsuda Chihiro) を元に
#ruby で作成された
#dcmodel-thum <http://www.gfd-dennou.org/arch/dcmodel/ClipBoard/dcmodel-thum/SIGEN.htm> (Yukiko Yamada) を整形したものである。
#
#
#== History
#
#* 2005/04/19 09:19:41
#  * スクリプト出力用のメソッド DCModelThumbnail.create_sample_rb を作成
#    * サンプルを出力する際に注意したこと。
#      * cvs によるバージョンを出力しておくこと。
#  * message においても、HTML, RD, RT のどの書式で変換するのか、
#    簡単に設定できるようにした。
#    * 現状では、"=begin" で始まれば RD, "=begin RT" や "=begin rt" で
#      始まれば RT, それ以外ならば HTML と判定する。
#      DCModelThumbnail.rd2html とは別に、
#      DCModelThumbnail.format_parser を作成し、"rd", "rt", "html" という
#      情報を返すようにしてある。
#  * DCModelThumbnail.rd2html において、"=begin" や "=end" が
#    付いているものも処理可能なように、beginend という引数を追加。
#  * infofile において、<ファイル名>:<修飾子>:<コメント> とする。
#    * 情報
#      * 左寄せ "<", 右寄せ ">", rd2html を掛ける "r", rt2html を掛ける "t",
#        なんらかの終了子が出るまで読み込む "m"
#        * "m" をつけた場合の複数行処理はまだ。
#  * infofile における、<ファイル名> の代替もの
#    * label
#      * 由ツールでの "title" に相当するもの。
#    * title 
#      * 由ツールとは仕様を変更し、本当に「title」の上書きを行える
#        ようにする。無い場合はスクリプト内部のものを使う。
#    * message
#     * スクリプト内部の @message (Array) に付け足す形で加えることができる。
#
#* 2005/04/15 02:59:12
#  * RD 書式を入れられるようにした。
#    * DCModelThumbnail.rd2html に RD 書式の文字列を入れると、HTML に変換
#      して返ってくるようにした。
#    * message 内に "=begin" が入っているのを探査して変換するようにする。
#    * 変換には外から強引に rd2 を呼んじゃう。
#    * <body> と </body> の間だけを呼んで再格納する。
#  * rd2html-ext-lib を強引に呼べるようにしておきたい。
#    * DCModelThumbnail.rd2html では、非常に強引に環境変数 RUBYLIB を
#      設定してライブラリを呼んでいる。
#    * $: に文字列を加えてもだめだったので…。
#
#* 2005/04/14 22:05:14
#  * ガッツリ内部を改造。基本的な仕様は余り変えず。
#  * 拡張機能
#    * スタイルシートの読み込みを可能に
#    * コメントの文字寄せを可能に
#    * 複数種の画像の処理を可能に
#  * 実際に HTML ファイルを出力するためのメソッドを作成
#    * 標準出力にも書き出せるようにしておく。
#  * HTML ファイルのための String オブジェクトを再生成するメソッドを
#    作成 (これは、上記の各値を設定するメソッドが呼ばれるたびに呼ばれる
#    ことを想定)
#  * 情報ファイルから情報を取得するためのメソッドを作成
#  * 初期化 initialize メソッドでは以下の事を行う。
#    * 以下の値のデフォルト値設定を行う。
#      * html ファイル名
#      * 情報ファイル
#      * 接頭子
#      * 絵のあるディレクトリ名
#      * 画像のファイルサイズ
#      * 横に並べる画像の数
#      * リンクの色等々
#      * htmlのヘッダ (タイトル)
#      * サムネイルタイトル
#      * サムネイルフッタ
#    * 上記の情報から HTML ファイルのための String オブジェクトを作成し、
#      インスタンス変数に格納しておく。
#  * 上記の各値を設定するためのメソッドを作成
#* 2005/04/11 16:00:23
#  * まずは見栄えを rdoc でドキュメント化できる形にする。
#
#
##################################################

require 'date'
require 'etc'
require 'kconv'
require 'pathname'

##################################################

#
#== DCModelThumbnail
#
# 「dcmodel お絵描きサムネイル HTML 作成スクリプト」の作成用のクラス
#
class DCModelThumbnail

  # 定数の設定
  #
  # CopyRight
  COPYRIGHT = "GFD Dennou Club"
  #
  # 項目の区切り文字
  INFO_DELIMITER = ":"



  attr_accessor :index, :index_ext, :infofile, :ext_list, :headlimit
  attr_accessor :figdir, :css, :img_width, :img_height, :figtable_num
  attr_accessor :html_author, :title, :message, :rd2_path, :rd2htmlextlib
  attr_accessor :tmp, :copyright
  #
  # これを呼ぶことで、最低限必要な情報が生成される。
  # 最終的には DCModelThumbnail.create メソッドを呼ぶことで
  # ファイルが作成される。
  #
  def initialize()
    #
    # copyright
    #
    @copyright = COPYRIGHT

    # html の置き場・ファイル名・拡張子。カレントディレクトリから
    #
    #    "#{@index}#{index_ext}" 
    #
    #に置かれる。
    @index     = "../htmlname"
    @index_ext = ".htm"

    # 絵のあるディレクトリ名。カレントディレクトリから 
    # "#{@figdir}" の場所を探査する。
    @figdir = "../figdir"

    # 情報ファイル, $PWD/${infofile}
    @infofile = File.basename(@index).chomp.strip + ".txt"

    # 拡張子
    @ext_list = ["gif", "png", "jpg", "jpeg"]

    # 接頭子制限 @headlimit = "figure_head"
    # これにより、@headlimit に当てはまらないものは無視される
    @headlimit = ""

    # スタイルシートファイル
    @css    = "/GFD_Dennou_Club/ftp/arch/dcmodel/htmltools/dcmodel.css"

    # rd2 ファイルのパス
    @rd2_path = "/usr/bin/rd2"

    # rd2-ext-lib へのライブラリのパス
    @rd2htmlextlib = "/GFD_Dennou_Club/ftp/arch/dcmodel/lib"

    # テンポラリファイル置き場 (UNIX 系ならば変える必要なし)
    @tmp     = "/tmp"

    # 画像ファイルサイズ
    @img_width  = 200
    @img_height = 150

    # 横にならべるファイル数
    @figtable_num = 3

    # html の作成者情報
    @html_author = username_from_gid

    # html ヘッダタイトル
    @title  = "タイトル"

    # 本体 (サムネイルの部分よりも上にメッセージ)
    @message = Array.new
    @message << <<-EndOfHeader
=begin
[((<地球流体電脳倶楽部|URL:http://www.gfd-dennou.org>))]
[((<dcmodel|URL:http://www.gfd-dennou.org/arch/dcmodel>)) |
((<dcmodel-tools|URL:http://www.gfd-dennou.org/arch/dcmodel/bin>))]

= #{@title}

== 目次

=== 中目次

 * リスト
   * 小項目

 * ((<情報ファイルへのリンク|URL:thum/htmlname.txt>))
 * 画像ファイルへのリンク((<URL:figdir>))
=end

    EndOfHeader
    @message << <<-EndOfHeader
=begin RT
caption = 表テスト

     , 人間, == , 犬 , ==
 ||  , 男  , 女 ,オス,メス

  x  , 1.0 , 2.0, 1.1, 1.2
  y  , 0.4 , 0.5, 0.3, 0.1

=end
    EndOfHeader
    @message << <<-EndOfHeader
    <br>
    実験設定などはここに書き込む.  <br>
    ここに書いた文字はセンタリングされてしまう.  <br>
    ちょっと恰好悪い. <br><br>
    <table BORDER=\\"0\\" cellspacing=\\"10\\" align=\\"center\\">
    <tr><td><small>
    テーブルをさらに作ってその中に書き込む. <br>
    そうするとこんな感じ. 
    </small></td></tr></table>
    <br><br>
    [<a href =\\"http://www.gfd-dennou.org/arch/dcmodel\\">HOME</a>]
    <hr>
    EndOfHeader

    debug(@message)
  end

  #
  # デバッグ出力用メソッド。組み込み関数 $DEBUG が真の場合 (つまり、
  # プログラムを $ ruby -d ./xxxxxx.rb として呼び出した場合) に
  # debug メソッドに代入された変数を出力する。
  #
  def debug(*args)
    p [caller.first, *args] if $DEBUG
  end
  private :debug

  #
  # 警告またはエラー。
  # err が nil や false の場合、mes に与えられたメッセージを
  # 警告として表示する。err が真の場合はそのメッセージの出力
  # と同時にエラーを発生させ、プログラムを終了させる。
  # errvar に変数を与えると、エラーの種類を指定できる。
  # quiet を true にすると、err が nil の場合、何も動作しなくなる。
  #
  def warn_or_err(mes=nil, err=nil, quiet=nil, errvar=nil)
    return nil if !mes

    errvar = RuntimeError if !errvar

    if err then
      raise errvar, "Error: #{mes}"
    elsif !quiet
      $stdout.print "[#{caller.first}] \n     Warning: #{mes}"
      return nil
    end
  end
  private :warn_or_err

  #
  #画像ファイル名・修飾情報・コメントを含む「情報ファイル」を作成するための
  #メソッド。
  #
  #figdir (String オブジェクト)
  #内の拡張子 ext_list (Array オブジェクト) にヒットするファイル名
  #をリストにし、infofile (String オブジェクト) に書き出す。
  #
  #headlimit (String オブジェクト) を渡すと、ファイル名の頭が
  #headlimit にヒットするものだけを拾い、リストからはその部分を
  #取り除くようにする。
  #
  #overwrite を true にすると、infofile が存在する場合でも
  #上書きする。また、quiet を true にすると警告メッセージを表示しない。
  #
  #delimiter (String オブジェクト) は情報ファイルの
  #画像ファイル名・修飾情報・コメントの区切り文字を設定する
  #ものであり、デフォルトは定数 INFO_DELIMITER にて設定されている。
  #一応変更できるようになっているが、原則的に変更しない
  #事を薦める。
  #
  #ファイルを作成したときは "create", 既存のものがすでに存在する
  #場合には "exist" を返し、既存のものが無く、なおかつ作成に失敗
  #した場合は false を返す。
  #
  #ここで作成されるファイルは DCModelThumbnail.info_get で取得される
  #ファイルの雛形である。
  #
  def info_make(figdir=nil, infofile=nil, ext_list=nil,
                headlimit=nil, overwrite=nil, delimiter=nil,
                quiet=nil, err=true)

    # infofile があり、overwrite が false の場合は終了。
    if File.exist?(infofile) && !overwrite then
      $stdout.print "[#{caller.first}] \n     Warning: Infofile \"#{infofile}\" already exist. So not generate infofile once again.\n" unless quiet
      return "exist"
    end

    # 引数の有効性の検証
    if !(str_and_notspace?(figdir)) then
      if err then
        raise ArgumentError, "Error: \"figdir\" is not specified. So \"infofile\" is not generated.\n. \n"
      elsif !quiet
        $stdout.print "[#{caller.first}] \n     Warning: \"figdir\" is not specified. So \"infofile\" is not generated.\n\n"
        return nil
      end

    elsif !(str_and_notspace?(infofile)) then
      if err then
        raise ArgumentError, "Error: \"infofile\" is not specified. So \"infofile\" is not generated.\n. \n"
      elsif !quiet
        $stdout.print "[#{caller.first}] \n     Warning: \"infofile\" is not specified. So \"infofile\" is not generated.\n\n"
        return nil
      end

    elsif !(array_and_notzero?(ext_list)) then
      if err then
        raise ArgumentError, "Error: \"ext_list\" is not specified. So \"infofile\" is not generated.\n. \n"
      elsif !quiet
        $stdout.print "[#{caller.first}] \n     Warning: \"ext_list\" is not specified. So \"infofile\" is not generated.\n\n"
        return nil
      end
    end

    # delimiter のセット
    if !(str_and_notspace?(delimiter)) then
      delimiter = INFO_DELIMITER
    end

    # headlimit の整形
    if !(str_and_notspace?(headlimit)) then
      headlimit = ""
    end

    # figdir から画像ファイル名一覧をとりだし, 配列 imgfiles へ代入
    imgfiles = Array.new
    Dir.foreach("#{figdir}") { |item|
      ext_list.each{ |ext|
        next if !(str_and_notspace?(ext))

        # ドットが付いていない場合はドットをつける
        if !(/^\.(.*)/ =~ ext) then
          ext = "." + ext.chomp.strip
        end

        # 拡張から有効か判定
        next unless /#{ext}$/i =~ item

        # headlimit から有効か判定
        if str_and_notspace?(headlimit) then
          if /^#{headlimit}(.+)$/ =~ item
            bodyname = $1
          else
            next
          end
        else
          bodyname = item
        end

        # imgfiles への格納
        #imgfiles.push( (File.basename(bodyname, ext) )  )
        imgfiles.push( bodyname )
      }
    }

    imgfiles = imgfiles.sort

    # infofile に書き込み
    ifile = open(infofile, "w")
    imgfiles.each{ |filename|
      ifile.print "#{filename}#{delimiter}#{delimiter}\n"
    }
    ifile.close

    return "create"
  end

  #
  # DCModelThumbnail.info_make によって作成される「情報ファイル」
  # infofile から画像ファイル名とコメント、および修飾情報を取り出し、
  # Array[Hash, ...] にして返す。
  # 各要素の Hash にはキーに値の種類の文字列を格納してある。
  # 以下は現在 Hash のキーとして取得されるものである。
  #
  #   * fig_name
  #     * ファイル名である。1 つ目の区切り文字 
  #       (DCModelThumbnail.info_make の INFO_DELIMITER 参照)
  #       よりも前の文字列をこのキーの値として取得する。なお、
  #       引数 headlimit が与えられる場合、情報ファイルの文字列の
  #       頭に headlimit を付加する。
  #   * fig_name_nohead
  #     * fig_name と同様にファイル名である。ただし、こちらは
  #       引数 headlimit が与えられる場合でも、情報ファイルから得られる
  #       文字列のみを格納する。
  #   * comment
  #     * サムネイルの個々の画像の下に付加されるコメントである。
  #       2 つ目の区切り文字以降の文字列がこのキーの値として取得される。
  #       なお、デフォルトでは改行文字までをコメントとして読み込むが、
  #       将来的には修飾情報に "m" が入る場合、"{" と "}" の間の文字
  #       を読み込むようにする予定である。(改行文字も含める)。
  #   * align
  #     * コメントの文字寄せ情報である。1つ目と2つ目の区切り文字の間に
  #       下記の文字列を入れることで、下記のそれに対応する値が格納される。
  #       なお、デフォルトでは "center" が格納される。
  #       * "<" : 左寄せを示し、値に "left" を格納する。
  #       * ">" : 右寄せを示し、値に "right" を格納する。
  #   * format
  #     * フォーマットの情報である。
  #       下記の文字列を入れることで、下記のそれに対応する値が格納される。
  #       なお、デフォルトでは "html" が格納される。
  #       * "r" : RD フォーマットであることを示す。
  #       * "t" : RT フォーマットであることを示す。
  #   * line
  #     * 情報ファイルの該当情報の「行数」が格納される。
  #       複数行が指定される場合は "4-10" というように格納される。
  #
  # delimiter (String オブジェクト) は情報ファイルの
  # 画像ファイル名・修飾情報・コメントの区切り文字を設定する
  # ものであり、デフォルトは定数 INFO_DELIMITER にて設定されている。
  # 一応変更できるようになっているが、原則的に変更しない
  # 事を薦める。
  #
  # quiet を真にすると、ファイルがない際にもメッセージを表示しない。
  #
  # err を真にすると、ファイルが見つからない際にエラー処理をする。
  #
  def info_get(infofile=nil, headlimit=nil, delimiter=nil, 
               quiet=nil, err=nil)

    # delimiter のセット
    if !(str_and_notspace?(delimiter)) then
      delimiter = INFO_DELIMITER
    end

    info_list = Array.new

    # ファイルが読み取れるかチェック
    if !(File.readable?(infofile)) then
        return warn_or_err("\"#{infofile}\" is not readable. \n",
                           err, quiet, ArgumentError)
    end

    # 実際にファイルを開く
    ifile = open(infofile, "r")

    line_num = 0
    ifile.each { |line|

      # 探索したデータの格納用配列とハッシュ
      info_part = Array.new
      info_hash = Hash.new

      # 行数を数える
      line_num += 1

      info_part = line.chomp.split(/#{delimiter}/, 3)
      info_hash['fig_name_nohead'] = info_part[0]
      info_hash['comment']         = info_part[2]
      info_hash['line']            = line_num.to_s

      # 行頭が "#" の場合はコメントアウトと考えて無視。
      if /^\s*#/ =~ info_part[0] then
        next
      end

      # info_hash['fig_name'] の設定
      if str_and_notspace?(headlimit) then
        info_hash['fig_name'] = headlimit.chomp.strip + info_part[0].chomp.strip
      else
        info_hash['fig_name'] = info_part[0]
      end

      #
      # 修飾子の解析 (万が一のことを考え、日本語も処理)
      #
      modifier = Kconv::toeuc(info_part[1].chomp.strip)

      #
      # 日本語らしきものが入っていたら警告またはエラー
      #
      if !(modifier == Kconv::toeuc(modifier)) ||
         !(modifier == Kconv::tojis(modifier)) ||
         !(modifier == Kconv::tosjis(modifier)) then

        warn_or_err(
                    "\"#{modifier}\" include 2 bite code, " +
                    "so this may not be parsed correctly.\n",
                    err, quiet, ArgumentError)
      end

      # 文字寄せ情報の解析
      if />/ =~ modifier.chomp.strip then
        info_hash['align'] = "right"
      elsif /</ =~ modifier.chomp.strip then
        info_hash['align'] = "left"
      else
        info_hash['align'] = "center"
      end

      # フォーマット情報の解析
      if /r/ =~ modifier.chomp.strip then
        info_hash['format'] = "rd"
      elsif /t/ =~ modifier.chomp.strip then
        info_hash['format'] = "rt"
      else
        info_hash['format'] = "html"
      end

      # 複数行を取得するかどうかの解析
      if /m/ =~ modifier.chomp.strip then
        warn_or_err(
                    "Still multi-line option is invalid.\n"
                    )
      end

      info_list.push(info_hash)
    }
    ifile.close

    return info_list
  end

  #
  # HTML のヘッダ部分の作成メソッド。相当する文字列を返す。
  # 作成した HTML は DCModelThumbnail.html_footer で得られる文字列で
  # 閉じられることを想定している。
  #
  def html_header()
    # @index のディレクトリから見た、生成スクリプトの相対的な位置
    generator = relative_str("#{$0}", @index)

    # @index のディレクトリから見た、css の相対的な位置
    css       = relative_str(@css, @index)

    header = <<-HTMLHEADER
<?xml version="1.0" encoding="euc-jp" ?>
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="ja" xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>#{@title}</title>
  <meta http-equiv="Content-Type" content="text/html; charset=x-euc-jp" />
  <meta name="Author" content="#{@html_author}" />
  <meta name="robots" content="noindex,nofollow" />
  <meta name="robots" content="noarchive" />
  <meta name="generator" content="#{generator}" />
  <link href="#{css}" type="text/css" rel="stylesheet" />
</head>
<body>
    HTMLHEADER
    return header
  end

  #
  # HTML のサムネイル部分 (画像の数に応じて繰り返す部分) を作成するメソッド。
  # 相当する文字列を返す。info_list には DCModelThumbnail.info_get で
  # 取得される形式の情報ファイルが与えられる。
  #
  # それ以外の形のデータが与えられる場合には警告を発して nil を返す。
  # ただし、err に true を与えると、エラーが生じる。
  # quiet が true の場合には警告メッセージも表示しない。
  #
  # 内部で DCModelThumbnail.html_thum_parts を呼び、各要素はそちらで
  # 作成する。
  #
  def html_thum(info_list=nil, quiet=nil, err=nil)

    #
    # 引数に与えられたデータの検証
    #
    if !array_and_notzero?(info_list) then
      return warn_or_err(
                         "\"info_list\" is invalid.\n",
                         err, quiet, ArgumentError)
    end

    #
    # table タグを作成
    #
    html_table = ""

    html_table = <<-HTMLEOF
    <table border="0" cellspacing="10" align="center">
    HTMLEOF

    #
    # 中身を作成
    #
    info_list.size.times{ |num|
      html_table << html_thum_parts(info_list, num, quiet, err)
    }

    #
    # もしも、info_list.size が @figtable_num で割り切れない場合、
    # 残り分の空白 td を作成する
    #
    remain = @figtable_num - (info_list.size % @figtable_num)
    td_width   = (100 / @figtable_num).truncate.to_s + "%"  # 一つの td の幅

    if !(remain == 0) then
      remain.times{ |time|
        html_table << <<-HTMLEOF
       <td width="#{td_width}" valign="top">
          &nbsp;
       </td>
        HTMLEOF
      }
      html_table << <<-HTMLEOF
      </tr>
      HTMLEOF
    end

    #
    # /table タグを作成
    #
    html_table << <<-HTMLEOF
    </table>
    HTMLEOF

    return html_table
  end


  #
  # HTML のサムネイル部分の内部 (テーブルの1要素毎) を作成するメソッド。
  # 相当する文字列を返す。info_list には DCModelThumbnail.info_get で
  # 取得される形式の情報ファイルを、num には何番目のデータなのかを
  # 示す Numerical オブジェクトを与える。
  #
  # それ以外の形のデータが与えられる場合には警告を発して nil を返す。
  # ただし、err に true を与えると、エラーが生じる。
  # quiet が true の場合には警告メッセージも表示しない。
  #
  def html_thum_parts(info_list=nil, num=nil, quiet=nil, err=nil)
    #
    # 引数に与えられたデータの検証
    #
    if !array_and_notzero?(info_list) then
      return warn_or_err(
                         "\"info_list\" is invalid.\n",
                         err, quiet, ArgumentError)
    end

    if !num.kind_of?(Integer)
      return warn_or_err(
                         "\"num\" is not integer.\n",
                         err, quiet, ArgumentError)
    end

    #
    # info_list からデータの取得
    #
    fig_name        = info_list[num]['fig_name'] # 画像ファイル名
    comment         = info_list[num]['comment']  # コメント
    align           = info_list[num]['align']    # 文字寄せ
    fig_name_nohead = info_list[num]['fig_name_nohead'] # 省略画像ファイル名
    format          = info_list[num]['format']   # comment のフォーマット
    debug(fig_name, comment, align, fig_name_nohead)

    #
    # format から、comment の変換処理
    #
    if /rd/ =~ format then
      comment_buff = rd2html(comment)
    elsif /rt/ =~ format then
      comment_buff = rd2html(comment)
    else
      comment_buff = "#{comment}"
    end
    comment = "#{comment_buff}"

    #
    # @index からの相対的な @figdir の設定
    #
    rel_figdir = relative_str(@figdir, @index)

    #
    # テーブルの配置によって <tr> のつけたしをおこなう。
    # (num はゼロからスタートすることに注意)
    # 左端 : @figtable_num * n       (remainder == 0)
    # 右端 : @figtable_num * n - 1   (remainder == @figtable - 1)
    #
    remainder  = num % @figtable_num    # 余り
    figtable_1 = @figtable_num - 1      # 列数 - 1
    td_width   = (100 / @figtable_num).truncate.to_s + "%"  # 一つの td の幅
    debug(num, remainder, figtable_1, @figtable_num, td_width)

    html_table_part  = ""

    # 左端用の <tr> タグ
    if remainder == 0 then
      html_table_part = <<-HTMLEOF
      <tr valign="center">
      HTMLEOF
    end

    # 画像ファイル名が "label" の場合には comment をそのまま書き出す。
    if /^label$/ =~ fig_name_nohead.chomp.strip then
      html_table_part << <<-HTMLEOF
       <td align="#{align}" width="#{td_width}" valign="center">
        <small>
         #{comment}
        </small>
       </td>
      HTMLEOF

    # 画像ファイル名が存在しない場合は空白を書き出す。
    elsif !(str_and_notspace?(fig_name_nohead)) then
      html_table_part << <<-HTMLEOF
       <td align="#{align}" width="#{td_width}" valign="top">
          &nbsp;
       </td>
      HTMLEOF

    # 上記以外の場合には、絵の縮小版とそのリンクを張り込む
    else
      html_table_part << <<-HTMLEOF
       <td align="center" width="#{td_width}" valign="top">
         <a href="#{rel_figdir}/#{fig_name}">
         <img src="#{rel_figdir}/#{fig_name}" border="1" width="#{@img_width.to_s}" height="#{@img_height.to_s}">
         </a>
         <br>
         <div align="#{align}">
           <small>
             #{fig_name_nohead}<br>
             #{comment}<br><br>
           </small>
         </div>
       </td>
      HTMLEOF
    end

    # 右端用の </tr> タグ
    if remainder == figtable_1 then
      html_table_part << <<-HTMLEOF
      </tr>
      HTMLEOF
    end

    return html_table_part
  end

  #
  # フッター作成用メソッド。相当する文字列を返す。
  # DCModelThumbnail.html_header で得られる文字列で始まる HTML を
  # 閉じることを想定している。
  #
  def html_footer()
    # @index のディレクトリから見た、生成スクリプトの相対的な位置
    generator = relative_str("#{$0}", @index)

    # @index のディレクトリから見た、情報ファイルの相対的な位置
    infofile  = relative_str(@infofile, @index)

    #
    # フッターとして書き出し
    #
    html_footer = <<-HTMLEOF
<hr size="1">
<center>
<small>
  This page is generated by <a href=\"#{generator}\">#{generator}</a>
  and <a href=\"#{infofile}\">#{infofile}</a>
  (#{Time.now.strftime("%Y/%m/%d %H:%M:%S")} #{username_from_uid})<br>
  Copyright &copy; #{@copyright} #{Time.now.strftime("%Y")}
</small>
</center>
</body>
</html>
    HTMLEOF
    return html_footer
  end

  #
  # HTML ファイルの最終的な作成メソッド。最後にこのメソッドを呼ぶことで
  # 作業が完了する。
  #
  # overwrite に false を与えると、上書きを禁止する。
  #
  # quiet を true にするとエラーを除く全てのメッセージが表示されなくなる。
  #
  # verbose を true にすると作業の進捗状況がメッセージとして出力される。
  #
  # err を true にした場合、意図しない動作が起きた場合にすぐに
  # エラーを起こす。
  #
  def create(overwrite=true, quiet=nil, verbose=true, err=true)

    #
    # サムネイルファイル名
    #
    index_file_name = @index.chomp.strip  +
                      @index_ext.chomp.strip

    #
    # 元ファイル削除 (overwrite が nil の場合はエラー)
    #
    if File.exist?(index_file_name)
      if overwrite then
        File.delete(index_file_name)
      else
        raise ArgumentError, "Error : \"#{index_file_name}\" exist already.\n"
      end
    end


    #
    # infofile の作成 (既に存在する場合はそのまま)。
    # DCModelThumbnail.info_make メソッドを呼ぶ
    #
    status = info_make(@figdir, @infofile, @ext_list,
                       @headlimit, nil, nil,
                       true, err)
    if verbose then
      if /create/ =~ status
        $stdout.print "  Message : Infofile \"#{@infofile}\" is created.\n"
      elsif /exist/ =~ status
        $stdout.print "  Message : Infofile \"#{@infofile}\" is already exist.\n"
      end
    end

    #
    # @message が Array オブジェクト以外の場合はエラーを返す。
    #
    if !@message.instance_of?(Array) then
      warn_or_err(
                  "\"message\" must be Array Object. " +
                  "Please \"message = Array.new\" initially.\n", 
                  true, nil, ArgumentError)
    end

    #
    # infofile から情報の取得。
    # DCModelThumbnail.info_get メソッドを呼ぶ。
    #
    info_list = info_get(@infofile, @headlimit, nil, quiet, err)
    #
    # infofile からの情報のうち、画像ファイル名を "title" にしている
    # ものに関して @title に上書きして、info_list から除く。
    #
    # infofile からの情報のうち、画像ファイル名を "message" にしている
    # ものに関して @message に追加して、info_list から除く。
    #
    info_list_buff = Array.new
    info_list.each{ |info_part|
      if /title/ =~ info_part['fig_name']
        @title = info_part['comment']
      elsif /message/ =~ info_part['fig_name']
        @message << info_part['comment']
      else
        info_list_buff << info_part
      end
    }
    info_list = Array.new
    info_list << info_list_buff
    info_list.flatten!          # 配列の平滑化 (1次元配列化)

    $stdout.print "  Message : Get information from \"#{@infofile}\".\n" if verbose

    #
    # @message のフォーマットを解析し、HTML に変換する。
    #
    html_message = Array.new
    @message.each{ |mess|
      format = format_parser(mess)

      if /rd/ =~ format then
        html_buff = rd2html(mess, true)
      elsif /rt/ =~ format then
        html_buff = rd2html(mess, true)
      else
        html_buff = mess
      end

      html_message << html_buff if html_buff
      debug(html_message)
    }
    debug(html_message)


    # 初期化
    html_entire = ""

    # ヘッダ部分
    $stdout.print "  Message : Generate HTML Header...." if verbose
    html_entire << html_header 
    $stdout.print "  done.\n" if verbose

    # 本文
    $stdout.print "  Message : Insert body messages...." if verbose
    html_message.each { |message|
      html_entire << message
    }
    $stdout.print "  done.\n" if verbose

    # サムネイル部分
    $stdout.print "  Message : Generate Thumbnail Lists...." if verbose
    html_entire << html_thum(info_list, quiet, true)
    $stdout.print "  done.\n" if verbose

    # フッタ部分
    $stdout.print "  Message : Generate HTML Footer...." if verbose
    html_entire << html_footer
    $stdout.print "  done.\n" if verbose

    # ファイルの書きだし
    $stdout.print "  Message : Output to #{index_file_name}...." if verbose
    ifile = open(index_file_name, "w")
    ifile.print html_entire
    ifile.close
    $stdout.print "  Successfull. \n" if verbose
  end

  #
  # mes で与えられる本文の行頭を解析し、その本文が HTML であるか、
  # RD であるか、RT であるのかを判別する。現在、以下のように判定している。
  #
  # * 行頭文字が「=begin」、「=begin RD」、「=begin rd」
  #   * RD のフォーマットであると判別
  #
  # * 行頭文字が「=begin RT」、「=begin rt」
  #   * RT のフォーマットであると判別
  #
  # * 上記以外
  #   * HTML フォーマットであると判別
  #
  # 返り値は "html", "rd", "rt" のいづれかである。なお、body に
  # String オブジェクト以外、もしくは完全に空白のみが入っている場合、
  # nil を返す。
  #
  def format_parser(body=nil)
    debug(body)
    if !(str_and_notspace?(body)) then
      return warn_or_err("\"body\" is not String Object.\n")
    end

    Kconv::toeuc(body)
    body_parts = body.split("\n")

    body_parts.each{ |line|
      next unless /\w+/e =~ line.chomp.strip
      if /=begin\s+(rt)/ie =~ line.chomp.strip
        return "rt"
      elsif /=begin/ie =~ line.chomp.strip
        return "rd"
      elsif /=begin\s+(rd)/ie =~ line.chomp.strip
        return "rd"
      else
        return "html"
      end
    }
    return nil
  end

  #
  # 引数 rd に与えられた文字列を rd2html を掛けた文字列として
  # 返す。デフォルトでは "=begin" や "=end" が存在しない文字列を
  # 想定している。この場合、rd 文字列を "=begin" と "=end"
  # ではさまれたものとして自動的に解釈する。もしも "=begin"
  # や "=end" が書き込まれた文字列を rd として与えたい場合は
  # 引数 beginend を true にすること。返される html は <body> 以上と
  # </body> 以下は省かれた本文部分のみである。
  #
  # もしも rd2 コマンドが存在しない場合には nil を返す。
  #
  # なお、現在は rd2html-ext-lib を必ず利用するようになっているので
  # インスタンス変数 rd2htmlextlib を適宜設定しないとまともに
  # 動かないので注意すること。
  #
  def rd2html(rd=nil, beginend=nil, quiet=nil, err=nil)
    debug(rd)

    if !(FileTest.executable?(@rd2_path)) then
      return warn_or_err(
                         "\"#{@rd2_path}\" is not excutable.\n",
                         err, quiet, ArgumentError)
    elsif !rd
      return warn_or_err(
                         "\"rd\" is invalid.\n",
                         err, quiet, ArgumentError)
    end

    debug(@rd2_path)

    # "=begin" や "=end" を消去するための処理
    rd_body = ""
    if beginend then
      rd_parts = rd.split("\n")
      debug(rd_parts)
      rd_parts.each { |line|
        if /\s*=begin\s+.*/ =~ line then
          next
        elsif /\s*=begin$/ =~ line then
          next
        elsif /\s*=end\s+.*/ =~ line then
          next
        elsif /\s*=end$/ =~ line then
          next
        else
          rd_body << line + "\n"
        end
      }
    else
      rd_body = "#{rd}"
    end
    debug(rd_body)

    # rd で得られた文字列を /tmp/dcmodel-thum-$$.rd に一時的に格納
    rdfile_tmp = @tmp + "/" + File.basename($0.to_s) + "-" + $$.to_s
    debug(rdfile_tmp)

    tmpfile = open(rdfile_tmp, "w")
    tmpfile.print "=begin\n"
    tmpfile.print rd_body
    tmpfile.print "\n=end\n"
    tmpfile.close

    debug(open(rdfile_tmp){|io| io.read})
#    print "#{open(rdfile_tmp){|io| io.read}}"

#    # ライブラリパスを $: に追加
#    @rd2lib.each{ |path|
#      next if !(str_and_notspace?(path))
##      $:.push(path)
#      $:.unshift(path)
#    }
#    debug($:)

    #
    # コマンドの文字列を整形
    #
    cmd  = "#{@rd2_path}"
    cmd << " -r rd/rd2html-ext-lib"
    cmd << " --with-css=#{@css}"
    cmd << " --with-part=\'RT:rt\'"
    cmd << " --with-part=\'HTML:html\'"
    cmd << " --out-code=euc"
    cmd << " --ref-extension"
    cmd << " --native-inline"
    cmd << " --head-element"
#    cmd << " --headline-secno"
#    cmd << " --enable-br"

    debug(cmd)
    html_org = IO.popen("export RUBYLIB=#{@rd2htmlextlib} ; #{cmd} #{rdfile_tmp}").read
    debug(html_org)

    html_body_each_line = html_org.split(/\n/)
    debug(html_body_each_line)

    html_body = ""
    bodyflag = false
    html_body_each_line.each{ |line|
      if /^.*<body.*$/ =~ line then
        bodyflag  = true
        next
      elsif /^.*<\/body.*$/ =~ line
        bodyflag  = false
      else
        html_body << line + "\n" if bodyflag
      end
    }

    # テンポラリファイルを削除
    File.delete(rdfile_tmp)

    debug(html_body)
    return html_body
  end

  #
  # target で与えられたパス (String オブジェクト) を from (String 
  # オブジェクト) から見た相対パスとして String オブジェクトで返す。
  # 内部で Pathname クラスを利用している。
  # 与えられるパスは絶対パスでも相対パスでもかまわない。
  #
  def relative_str(target=nil, from=nil)
    return nil    unless str_and_notspace?(target)
    return target unless str_and_notspace?(from)

    from_dir     = File.dirname(from)
    target_dir   = File.dirname(target)
    target_base  = File.basename(target)

    from_ab_path   = Pathname.new(File.expand_path(from_dir))
    target_ab_path = Pathname.new(File.expand_path(target_dir))

    target_re_path = target_ab_path.relative_path_from(from_ab_path)

    result = target_re_path.to_s + "/" + target_base

    return result
  end

  #
  # サンプルスクリプト出力用のメソッド。
  # この DCModelThumnail クラスに依存するサンプルスクリプトを
  # 引数 filename という名前で出力する。実際には、このファイル
  # を編集・実行することでサムネイルが作成される寸法である。
  #
  def create_sample_rb(filename)
    if !(str_and_notspace?(filename)) then
      return warn_or_err("filename is invalid.\n",
                         true, nil, ArgumentError)
    end
    
    rb_file_body = <<-EndOfFile
#!/usr/bin/env ruby
#
#= dcmodel thumnail generate ruby script
#
#  Editor :: #{username_from_uid}
#  Version:: #{Time.now.strftime("%Y/%m/%d %H:%M:%S")}
#
#== Overview
#
#This file is generate by following ruby script automatically.
#
#      #{File.expand_path($0.to_s)}
#
#Please edit this file according to your purpose.
#
#== Usage
#
#
##################################################

require "#{File.expand_path($0.to_s)}"

######################################################
if $0 == __FILE__ then
  thumb = DCModelThumbnail.new

#  thumb.copyright = "#{@copyright}"
  thumb.index     = "#{@index}"
#  thumb.index_ext = "#{@index_ext}"
  thumb.infofile  = "#{@infofile}"
#  thumb.ext_list.push("bmp")
#  thumb.headlimit = "headlimit_"
#  thumb.figdir    = "#{@figdir}"
#  thumb.css       = "#{@css}"
#  thumb.rd2_path  = "#{@rd2_path}"
#  thumb.rd2htmlextlib = "#{@rd2htmlextlib}"
#  thumb.img_width  = #{@img_width}
#  thumb.img_height = #{@img_height}
#  thumb.figtable_num = #{@figtable_num}
#  thumb.html_author  = "#{@html_author}"
  thumb.title     = "#{@title}"
  thumb.message   = Array.new
  thumb.message   << <<-MSG
#{@message[0]}
  MSG

  thumb.message   << <<-MSG
#{@message[1]}
  MSG

  thumb.message   << <<-MSG
#{@message[2]}
  MSG
  thumb.create
end

    EndOfFile

    #
    # ファイルの作成
    #
    ifile = open(filename, "w")
    ifile.print "#{rb_file_body}"
    ifile.close

    #
    # パーミッションの設定
    #
    File.chmod(0755, filename)

  end

  #
  # 以降は Private メソッド
  #

  #
  # 引数 uid に対応するユーザ名 (ログイン名) を返す。
  # uid に nil を与えた場合はプロセスの uid に対応するユーザ名 (ログイン名)
  # を返す。uid が無効なものである場合、エラーを返す。
  #
  def username_from_uid(uid=nil)
    unless uid
      pw = Etc.getpwuid(Process.uid) or return nil
    else
      pw = Etc.getpwuid(uid) or return nil
    end

    user_name = pw.name
    return user_name
  end
  private :username_from_uid

  #
  # 引数 gid に対応するユーザ名 (ログイン名) を返す。
  # gid に nil を与えた場合はプロセスの gid に対応するユーザ名 (ログイン名)
  # を返す。gid が無効なものである場合、エラーを返す。
  #
  def username_from_gid(gid=nil)
    unless gid
      pw = Etc.getpwuid(Process.gid) or return nil
    else
      pw = Etc.getpwuid(gid) or return nil
    end

    user_name = pw.name
    return user_name
  end
  private :username_from_gid


  #
  # 代入された変数が、文字列で、且つ空白文字のみではないことを
  # 調べるメソッド。日本語であっても、文字列が入っていれば true を返す。
  #
  def str_and_notspace?(obj)
    debug(obj)

    if !obj.instance_of?(String) then
      return false
    end

    # 日本語の文字列も対応できるように
    Kconv::toeuc(obj)

    if /\w+/e =~ obj.chomp.strip then
      return true
    else
      return false
    end
  end
  private :str_and_notspace?

  #
  # 代入された変数が、配列で、且つゼロ配列ではないことを
  # 調べるメソッド
  #
  def array_and_notzero?(obj)
    debug(obj)

    if obj.instance_of?(Array) && obj.size > 0 then
      return true
    else
      return false
    end

  end
  private :array_and_notzero?

end

######################################################
if $0 == __FILE__
  thumb = DCModelThumbnail.new

  sample_rb_file = "#{File.basename($0, ".*")}" + "-make.rb"

  if File.exist?(sample_rb_file)
    raise IOError, "Error : \"#{sample_rb_file}\" is already exist.\n"
  end

  $stdout.print "  Message : Generating Sample Ruby script \"#{sample_rb_file}\"...."
  thumb.create_sample_rb(sample_rb_file)
  $stdout.print "  done.\n"

# 
# #  thumb.copyright = "hoge"
# #  thumb.index     = "htmlname"
# #  thumb.index_ext = ".htm"
# #  thumb.infofile  = "hogehoge"
#   thumb.ext_list.push("bmp")
#   thumb.headlimit = "hs94_T42L20_TAve300_VisOrd8_"
# #  thumb.figdir    = ""
#   thumb.css       = "../dcmodel.css"
# #  thumb.rd2_path  = "/usr/local/bin/rd2"
#   thumb.rd2htmlextlib = "/home/morikawa/Documents/ruby/dcmodel_thum/sample"
# #  thumb.img_width  = 240
# #  thumb.img_height = 180
#   thumb.figtable_num = 2
# #  thumb.html_author  = "morikawa"
# #  thumb.title     = "サンプル"
# #  thumb.message   = <<-MSG
# #ほげほげ
# #   MSG
# 
# 
#   thumb.create

end
