#!ruby -Ks # -*- mode: ruby; encoding: sjis -*- # Last updated: <2014/12/17 02:04:45 +0900> require 'dxruby' require 'benchmark' # BDFフォント描画用クラス # # @version 0.0.3 # # @note iso8859, jisx0208, iso10646 のみに対応 # # @example # require 'dxruby' # require_relative 'bdffont' # bdf = BDFFont.new("mplus_j12r_jisx0208.bdf") # bdf.add("mplus_f12r_iso8859.bdf") # Window.loop do # x, y = 10, 10 # Window.draw_bdffont(x, y, "This is a BDF font", bdf) # y += bdf.height # Window.draw_bdffont(x, y, "The quick brown fox jumps over the lazy dog.", bdf) # y += bdf.height # Window.draw_bdffont(x, y, "1234567890 ! = - +", bdf) # y += bdf.height # Window.draw_bdffont(x, y, "JAPANESE MESSAGE OK", bdf) # end # class BDFFont # @private # BDFフォント1文字分の情報を格納する構造体 BDFChar = Struct.new(:startchar, :encoding, :image, :ofsx, :ofsy, :w, :h, :swidth, :sheight, :dwidth, :dheight) # @private # BDFフォント1ファイル分のデータを格納するクラス class BDFdata # @return [Hash] BDFフォント1文字分用クラスを複数格納したハッシュ attr_reader :bdf_dic # @return [Integer] 文字コード。:iso8859, :jisx0208, :iso10646 を取り得る attr_reader :charset # @return [Integer] バウンディングボックスの横幅 attr_reader :width # @return [Integer] バウンディングボックスの縦幅 attr_reader :height # @return [Integer] バウンディングボックスのxオフセット attr_reader :ofsx # @return [Integer] バウンディングボックスのyオフセット attr_reader :ofsy # @return [Integer] ポイントサイズ attr_reader :pt_size # @return [Integer] 横方向のDPI attr_reader :xdpi # @return [Integer] 縦方向のDPI attr_reader :ydpi # @return [Integer] ピクセルサイズ attr_reader :pixel_size # コンストラクタ # # @param [String] bdffilename BDFフォントファイル名 # @param [Array] col 文字色。a,r,g,b を並べた配列で指定 def initialize(bdffilename, col = [255, 255, 255, 255]) @bdf_dic = {} @pt_size = 0 @xdpi, @ydpi = 0, 0 @ofsx, @ofsy = 0, 0 @width, @height = 0, 0 @pixel_size = 0 @charset = nil @cahrs = 0 # ファイル読み込み f = open(bdffilename) filedata = f.read f.close bdfc = nil startchar = nil charcode = -1 bitmap_read = false bitpat = [] filedata.each_line do |l| # 1行ずつ処理 l.chomp! if startchar # 1文字分読み取り中 if bitmap_read if l.index('ENDCHAR') == 0 # ビットマップデータ定義終了。imageを生成 bdfc.image = make_image(bdfc.w, bdfc.h, bitpat, col) @bdf_dic[bdfc.encoding] = bdfc bitmap_read = false startchar = nil else # ビットマップ配列を溜める bitpat.push(l) end else # 1文字文のプロパティを読み取り p = l.split(' ') case p[0] when 'BITMAP' bitmap_read = true when 'ENCODING' bdfc.encoding = p[1].to_i when 'SWIDTH' bdfc.swidth, bdfc.sheight = p[1].to_i, p[2].to_i when 'DWIDTH' bdfc.dwidth, bdfc.dheight = p[1].to_i, p[2].to_i when 'BBX' bdfc.w, bdfc.h = p[1].to_i, p[2].to_i bdfc.ofsx, bdfc.ofsy = p[3].to_i, p[4].to_i end end else # BDFの全体情報を読み取り p = l.split(' ') case p[0] when 'STARTCHAR' # 文字のビットマップデータ定義部分開始 startchar = p[1] bdfc = BDFChar.new() bdfc.startchar = startchar bitmap_read = false bitpat.clear when 'SIZE' # ポイント、dpi @pt_size, @xdpi, @ydpi = p[1].to_i, p[2].to_i, p[3].to_i when 'FONTBOUNDINGBOX' # バウンディングボックス @width, @height = p[1].to_i, p[2].to_i @ofsx, @ofsy = p[3].to_i, p[4].to_i when 'PIXEL_SIZE' # ピクセルサイズ @pixel_size = p[1].to_i when 'CHARSET_REGISTRY' # 文字コード @charset = get_charset(p[1]) when 'CHARS' # 全文字数 @chars = p[1].to_i when 'ENDFONT' # 全データ読み取り終了 break end end end # puts @bdf_dic.length end # @private # 16進数文字列の配列からImageを生成して返す # # @param [Integer] w 横幅 # @param [Integer] h 縦幅 # @param [Array] dt 16進数文字列が入った配列 # @param [Array] col 文字色。a,r,g,b を並べた配列で指定 # @return [Object] Imageオブジェクト def make_image(w, h, dt, col) img = Image.new(w, h) msk = 1 << (dt[0].length * 4 - 1) v = 0 h.times do |y| v = dt[y].hex w.times do |x| img[x, y] = col if (v & (msk >> x)) != 0 end end return img end # @private # charsetのシンボルを返す # # @param [String] str charset種類を含む文字列 # @return [Integer] charsetを示すシンボル def get_charset(str) cst = str.gsub('"', '').downcase if cst.index("jisx0208") == 0 return :jisx0208 elsif cst.index("jisx0213") == 0 return :jisx0213 elsif cst.index("iso10646") == 0 return :iso10646 end return :iso8859 end end # @return [Integer] バウンディングボックスの縦幅(最大値) attr_reader :height # @return [Integer] バウンディングボックスのyオフセット attr_reader :ofsy # コンストラクタ # # @param [String] bdffilename BDFフォントファイル名 # @param [Array] col 文字色。a,r,g,b を並べた配列で指定 # def initialize(bdffilename, col = [255, 255, 255, 255]) @bdffiles = [] bdfd = BDFdata.new(bdffilename, col) @bdffiles.push(bdfd) @width = bdfd.width @height = bdfd.height @ofsy = bdfd.ofsy end # BDFファイルを追加。 # # 漢字BDFフォント(jisx0208)でクラスを生成した後、 # 英数字BDFフォント(iso8859)を一緒に含める時に使う。 # # @param [String] bdffilename BDFフォントファイル名 # @param [Array] col 文字色。a,r,g,b を並べた配列で指定 # def add(bdffilename, col = [255, 255, 255, 255]) bdfd = BDFdata.new(bdffilename, col) @bdffiles.push(bdfd) @width = bdfd.width if @width < bdfd.width @height = bdfd.height if @height < bdfd.height @ofsy = bdfd.ofsy if @ofsy.abs < bdfd.ofsy.abs end # 配列として指定した際に、BDFフォント1文字分用のクラスを返す # # @param [Integer] index 配列添字 # @return [Object] BDFフォント1文字分のクラス # @return [nil] nilなら登録文字は見つからなかった # def [](index) @bdffiles.each do |d| code = nil case d.charset when :jisx0208 code = self.sjis2jisx0208(index.ord) when :jisx0213 # 未対応。変換方法が分からない code = self.sjis2jisx0208(index.ord) # code = index.encode("UTF-8", "Windows-31J").ord # code = index.encode("EUC-JISX0213", "Windows-31J").ord # code = index.encode("EUC-JP-2004", "Windows-31J").ord when :iso10646 code = index.encode("UTF-8", "Windows-31J").ord else code = index.ord end return d.bdf_dic[code] if d.bdf_dic.has_key?(code) end return nil end # @private # SJISコードをJISX0208コードに変換して返す # @param [Integer] code SJISコード # @return [Integer] JISX0208コード def sjis2jisx0208(code) code -= 0x4000 if code >= 0x0e000 ub = (code & 0x0ff00) >> 8 code = ((ub - 0x081) << (1+8)) + (code & 0x00ff) code -= 1 if (code & 0x00ff) >= 0x80 if (code & 0x00ff) >= 0x009e code += (0x0100 - 0x009e) else code -= 0x0040 end code += 0x2121 return code end end # Windowクラスに追加するBDFフォント描画用メソッド module Window # BDFフォントを描画 # @param [Number] x 描画x座標 # @param [Number] y 描画y座標 # @param [String] str 描画文字列 # @param [Object] bdf BDFフォントクラス # @param [Integer] z 描画優先順位 def self.draw_bdffont(x, y, str, bdf, z = 0) str.split('').each do |code| b = bdf[code] unless b == nil x1 = x + b.ofsx y1 = y + bdf.height + bdf.ofsy - b.h - b.ofsy Window.draw(x1, y1, b.image, z) x += b.dwidth end end end end # Imageクラスに追加するBDFフォント描画用メソッド class Image # BDFフォントを描画 # @param [Number] x 描画x座標 # @param [Number] y 描画y座標 # @param [String] str 描画文字列 # @param [Object] bdf BDFフォントクラス def draw_bdffont(x, y, str, bdf) str.split('').each do |code| b = bdf[code] unless b == nil x1 = x + b.ofsx y1 = y + bdf.height + bdf.ofsy - b.h - b.ofsy self.draw(x1, y1, b.image) x += b.dwidth end end end end if $0 == __FILE__ #テストコード lst = [ "This is a BDF font. !\"\#$%&'()*+,-./0123456789:;<=>?@", "A B C D EFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~", "あいうえおかきくけこ、新しい朝が来た。希望の朝だ", "親譲りの無鉄砲で小供の時から損ばかり。智に働けば角が立つ。" ] col = [255, 128, 255, 128] # BDFフォントクラスを生成 bdfs = [] Benchmark.bm do |x| x.report { case 0 when 0 bdf = BDFFont.new('bdf/mplus_j10r_jisx0208.bdf', col) bdf.add('bdf/mplus_f10r_iso8859.bdf', col) bdfs.push(bdf) bdf = BDFFont.new('bdf/umplus_j10r_iso10646.bdf', col) bdfs.push(bdf) bdf = BDFFont.new('bdf/mplus_j12r_jisx0208.bdf', col) bdf.add('bdf/mplus_f12r_iso8859.bdf', col) bdfs.push(bdf) when 1 bdf = BDFFont.new('bdf/milkjf_k16_jisx0208.bdf', col) bdf.add('bdf/milkjf_8x16_iso8859.bdf', col) bdfs.push(bdf) bdf = BDFFont.new('bdf/shnmk16_jisx0208.bdf', col) bdf.add('bdf/shnm8x16a_iso8859.bdf', col) bdfs.push(bdf) when 2 bdf = BDFFont.new('bdf/courR12.bdf', col) bdfs.push(bdf) bdf = BDFFont.new('bdf/courR14.bdf', col) bdfs.push(bdf) when 3 # ---------------------------------------- # 未対応 bdf = BDFFont.new('bdf/warabi12-1.bdf', col) bdfs.push(bdf) bdf = BDFFont.new('bdf/jiskan16-2004-1_jisx0213.bdf', col) bdf.add('bdf/jiskan16-2000-2_jisx0213.bdf', col) bdfs.push(bdf) end } end # 画像に描画してみる例 image = Image.new(640, 240, [255, 64, 64, 64]) x, y = 0, 0 bdfs.each do |bdf| lst.each do |s| image.draw_bdffont(x, y, s, bdf) y += bdf.height end y += bdf.height end Window.bgcolor = [96, 96, 96] Window.loop do break if Input.keyPush?(K_ESCAPE) # 画面に描画してみる例 x, y = 16, 0 bdfs.each do |bdf| lst.each do |s| Window.draw_bdffont(x, y, s, bdf) y += bdf.height end y += bdf.height end Window.draw(x, 240, image) bar = Window.height * Window.getLoad / 100.0 Window.drawBoxFill(0, 0, 2, bar, C_RED) end end