add kage.rb, kageserver.rb, csf.rb, stroke.rb
authoreto <eto>
Wed, 12 Mar 2003 08:33:49 +0000 (08:33 +0000)
committereto <eto>
Wed, 12 Mar 2003 08:33:49 +0000 (08:33 +0000)
src/csf.rb [new file with mode: 0755]
src/kage.rb [new file with mode: 0755]
src/kageserver.rb [new file with mode: 0755]
src/stroke.rb [new file with mode: 0755]

diff --git a/src/csf.rb b/src/csf.rb
new file mode 100755 (executable)
index 0000000..c323495
--- /dev/null
@@ -0,0 +1,121 @@
+#!/usr/bin/env ruby
+# CSF module by eto 2002-1115
+
+require 'sgl'
+require 'kconv'
+require 'uconv'
+
+module StrokeFont
+  CSF_FONT_DIR = 'd:/work/chise/csf/'
+  CSF_DEFAULT_FILE  = 'KST32B.CSF1'
+  CSF_KOUKOTSU_FILE = 'KST32ZX.CSF1'
+
+  class CSFStrokeMaker #======================================================================
+    DEST_WIDTH = 1000 #\91å\82«\82³\82ð\82¨\82¨\82æ\82»1000x1000\82É\90³\8bK\89»\82·\82é\81B
+    ORG_WIDTH = 32 #\8c³\82Ì\83T\83C\83Y\82Í\81A\89¡30\81~\8fc32
+    def initialize
+      @x, @y, @nx, @ny = 0, 0, 0, 0
+      @strokes = Strokes.new
+    end
+    attr_reader :strokes
+    def move_to_x(x) @x = x; @nx = x; end
+    def draw_to_x(x) @nx = x; drawline; @x = @nx; @y = @ny; end
+    def next_x_to(x) @nx = x; end
+    def move_to_y(y) @y = y; @ny = y; end
+    def draw_to_y(y) @ny = y; drawline; @x = @nx; @y = @ny; end
+    def drawline()
+      @strokes.add_line(t(@x), t(@y), t(@nx), t(@ny))
+    end
+    def t(a) a*DEST_WIDTH/ORG_WIDTH; end
+  end
+
+  class CSFParser #======================================================================
+    def self.parse(str) #Strokes\82ð\95Ô\82·
+      return Strokes.new if str == nil
+      sm = CSFStrokeMaker.new
+      (0...str.length).each {|i|
+        n = str[i]
+       if 0x21 <= n && n <= 0x26
+         sm.move_to_x(n - 0x21)
+       elsif 0x28 <= n && n <= 0x3f
+         sm.move_to_x(n - 0x28 + 6)
+       elsif 0x40 <= n && n <= 0x5b
+         sm.draw_to_x(n - 0x40)
+       elsif 0x5e <= n && n <= 0x5f
+         sm.draw_to_x(n - 0x5e + 28)
+       elsif 0x60 <= n && n <= 0x7d
+         sm.next_x_to(n - 0x60)
+       elsif 0x7e == n
+         sm.move_to_y(n - 0x7e)
+       elsif 0xa1 <= n && n <= 0xbf
+         sm.move_to_y(n - 0xa1 + 1)
+       elsif 0xc0 <= n && n <= 0xdf
+         sm.draw_to_y(n - 0xc0)
+       end
+      }
+      return sm.strokes
+    end
+  end
+
+  class CSFGlyph #======================================================================
+    def initialize(code, stroke)
+      @code = code
+      @stroke_str = stroke
+      @strokes = nil
+    end
+    attr_reader :strokes
+    def parse()
+      return if @strokes
+      @strokes = CSFParser.parse(@stroke_str)
+    end
+    def init
+      parse if @strokes.nil?
+    end
+  end
+
+  class CSFFont #======================================================================
+    def initialize(file=CSF_DEFAULT_FILE)
+      @file = CSF_FONT_DIR + file
+      @glyphs = []
+      read_file
+      @rend = nil
+      @rend = StrokesRenderer.new
+      @rend.hsv = [50, 100, 100]
+    end
+    def read_file()
+      open(@file) {|f|
+       while(line = f.gets)
+         next if line =~ /^\*/
+         c, s = line.split
+         code = c.hex #JIS\82Ì\92l\82ª\90\94\92l\82Å\82Í\82¢\82é
+         @glyphs[code] = CSFGlyph.new(code, s)
+       end
+      }
+    end
+    def init(code)
+      glyph = @glyphs[code]
+      return if glyph == nil
+      glyph.init()
+      glyph.parse
+      @rend.set_strokes(glyph.strokes)
+    end
+    def draw(code) #\88ø\90\94\82É\82ÍJIS\82ð\90\94\92l\89»\82µ\82½\82à\82Ì\82ª\82Í\82¢\82é
+      glyph = @glyphs[code]
+      return if glyph == nil
+      @rend.draw
+    end
+    def print(code)
+      jis = JISX0208.new
+      char = jis.get_char(code)
+      printf("[%s][%04x]\n", char.nil? ? "nil" : char.map_sjis, code)
+    end
+    def ucs_to_jis(ucs)
+      char = Character.get(ucs)
+      j = char.japanese_jisx0208
+      return j
+    end
+  end
+
+end
+
+#----------------------------------------------------------------------\8fI\97¹
diff --git a/src/kage.rb b/src/kage.rb
new file mode 100755 (executable)
index 0000000..2c4baf1
--- /dev/null
@@ -0,0 +1,98 @@
+#!/usr/bin/env ruby
+# KageFont library by eto 2003-0311
+
+require 'sgl'
+require 'kageserver'
+
+#こんな感じのフォーマットになっている。
+#<?xml version="1.0"?>
+#<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+#<svg viewBox="0 0 1000 1000">
+#<path d="M 50,540 950,255 " style="fill: none; stroke: black; stroke-width: 10; stroke-linecap: round;"/>
+#<path d="M 330,50 330,900 M 330,900 Q 330,950 380,950 M 380,950 840,950 M 840,950 Q 890,950 915,850 " style="fill: none; stroke: black; stroke-width: 10; stroke-linecap: round;"/>
+#</svg>
+
+module StrokeFont
+  class KageParser #======================================================================
+    def self.parse(svg)
+      @strokes = Strokes.new
+      lines = svg.split(/\n/)
+      lines.each {|line|
+       next unless line =~ /^<path/
+       if line =~ /d=\"([a-zA-Z0-9, ]+)\"/
+         KageParser.parse_path($1)
+       end
+      }
+      return @strokes
+    end
+    def self.parse_path(str)
+      #M 50,540 950,255 
+      #M 330,50 330,900 M 330,900 Q 330,950 380,950 M 380,950 840,950 M 840,950 Q 890,950 915,850 
+      px, py = -1, -1
+      str.split.each {|par|
+       next if par.length == 1 #一文字はとばします。本来はちゃんとベジェを展開する。
+       if par =~ /[,0-9]+/
+         sx, sy = par.split(/,/)
+         x, y = sx.to_i, (1000 - sy.to_i)
+         if px != -1
+           @strokes.add_line(px, py, x, y)
+         end
+         px, py = x, y
+       end
+      }
+    end
+  end
+
+  class KageGlyph #====================================================================== てなかんじ
+    def initialize(code, svg)
+      @code = code
+      @svg = svg
+      @strokes = nil
+    end
+    attr_reader :strokes
+    def parse
+      return if @strokes
+      @strokes = KageParser.parse(@svg)
+    end
+    def init
+      parse if @strokes.nil?
+    end
+  end
+
+  class KageFont #======================================================================
+    def initialize
+      @server = KageServer.instance
+      @glyphs = []
+      @cache_list = @server.list_cache
+      @rend = nil
+      @rend = StrokesRenderer.new
+      @rend.hsv = [13, 100, 100]
+    end
+    attr_reader :cache_list
+    def get(code)
+      return @glyphs[code] if @glyphs[code]
+      svg = @server.get(code)
+      return nil if svg.nil?
+      @glyphs[code] = KageGlyph.new(code, svg)
+    end
+    def init(code)
+      glyph = get(code)
+      return if glyph.nil?
+      glyph.init
+      glyph.parse
+      @rend.set_strokes(glyph.strokes)
+    end
+    def draw(code)
+      glyph = get(code)
+      return if glyph.nil?
+      @rend.draw
+    end
+    def print(code)
+      char = Character.new(@code)
+      printf("[%s][%04x]\n", char.nil? ? "nil" : char.map_sjis, code)
+    end
+  end
+
+end
+
+#----------------------------------------------------------------------end.
diff --git a/src/kageserver.rb b/src/kageserver.rb
new file mode 100755 (executable)
index 0000000..dc1295d
--- /dev/null
@@ -0,0 +1,142 @@
+#!/usr/bin/env ruby
+
+$LOAD_PATH << '../ruby/src'
+require 'chise'
+include CHISE
+require 'singleton'
+require 'net/http'
+require 'uri'
+require 'network' #漢字間接続のネットワークを計算する
+
+class KageServer #======================================================================
+  include Singleton
+  TYPES = "skeleton mincho gothic".split
+  SKELETON = 0
+  MINCHO = 1
+  GOTHIC = 2
+  HOME_DIR = "d:/work/chise/"
+  CACHE_DIR = HOME_DIR + "cache_kage"
+  URL  = "http://192.168.2.60:5100/"
+  #URL = "http://218.219.209.16:5100/"
+  def initialize(url=URL)
+    @url = url
+    @glyphs = []
+    @use_cache = true #デフォルト: cacheに存在する場合はcacheから引き出す。
+    @offline = false #テスト用
+    @offline = true #テスト用
+    Dir.mkdir(CACHE_DIR) unless FileTest.directory?(CACHE_DIR)
+  end
+  attr_accessor :url, :use_cache
+  def filename(num, type=SKELETON) sprintf("u%04x.%s", num, TYPES[type]) end
+  def cache_file(num, type=SKELETON) CACHE_DIR+"/"+filename(num, type)+".svg" end
+
+  def list_cache
+    ar = []
+    Dir.chdir(CACHE_DIR)
+    Dir.glob("*.svg").each {|file|
+      if file =~ /^u([0-9a-fA-F]+).skeleton.svg$/
+       code = $1.hex
+       ar << code
+      end
+    }
+    ar
+  end
+
+  def get(num, type=SKELETON)
+    return open(cache_file(num, type)).read if FileTest.exist?(cache_file(num, type))
+    svg = get_http(num, type)
+    return svg if svg
+    return nil
+  end
+  def get_http(num, type=SKELETON)
+    return nil if @offline
+    uri = URI.parse(URL + filename(num, type))
+    p ['uri', uri.to_s]
+    Net::HTTP.version_1_1   # declear to use 1.1 features.
+    Net::HTTP.start( uri.host, uri.port ) {|http|
+      response, body = http.get('/'+filename(num, type)+".svg")
+      if body
+       if error?(body)
+         p ['error', uri.to_s]
+         return nil
+       else
+         store_cache(num, type, body)
+         return body
+       end
+      end
+    }
+    return nil
+  end
+  def store_cache(num, type, svg)
+    open(cache_file(num, type), "w") {|f|
+      f.print svg
+    }
+  end
+  def error?(svg)
+    (svg =~ /<!-- error -->/)
+  end
+  def read_list
+    h = {}
+    open("kage-list.txt"){|f|
+      while line = f.gets
+       if line =~ /u([0-9a-f]+)\.skeleton/
+         code = $1
+         num = code.hex
+         error = false
+         error = true if line =~ /error/
+         h[num] = error
+       end
+      end
+    }
+    return h
+  end
+  def get_all
+    #error_h = read_list
+    STDOUT.binmode
+    @kn = KanjiNetwork.new
+    @kl = KanjiList.instance
+    #list = @kl.awase(0)
+    #list = @kl.awase(1)
+    list = @kl.joyo
+    @kn.make_network(list)
+    nodes, edges = @kn.nodes_and_edges
+    ar = []
+    nodes.each {|ch|
+      char = ch.char
+      num = char.to_i
+      #next if 0xffff < num
+      ar << num
+    }
+    get_ar(ar)
+  end
+  def get_ar(ar)
+    ar.each {|num| #intの数列
+      char = Character.get(num)
+      ch = char.to_s
+      er = char.to_er
+      #TYPES.each_with_index {|type, index|
+      #(0..2).each {|index|
+      (0..0).each {|index|
+       result = get(num, index) #cacheに保存するべしと。
+       next if result
+       err = "error"
+       print "#{er}    #{ch}   #{err}\n"
+      }
+    }
+  end
+  def test_kanji
+    char = "&CDP-8BA5;".de_er
+    #p char.inspect_all
+    #str = (char.to_s+"真")
+    str = (char.to_s+"直")
+    p str.find
+  end
+end
+
+if $0 == __FILE__ #======================================================================
+  ks = KageServer.instance
+  #print ks.get(0x4e03)
+  ks.get_all
+end
+
+#----------------------------------------------------------------------end.
diff --git a/src/stroke.rb b/src/stroke.rb
new file mode 100755 (executable)
index 0000000..b6f379b
--- /dev/null
@@ -0,0 +1,251 @@
+#!/usr/bin/env ruby
+# StrokeFont library by eto 2003-0311
+
+require 'sgl'
+require 'kage'
+require 'csf'
+
+module StrokeFont
+  class StrokesRenderer #======================================================================
+    def initialize
+      @start_time = nil
+      @strokes = nil
+      @hsv = [0, 0, 100]
+      init
+    end
+    attr_accessor :hsv
+    def init() @start_time = Time.now; end
+    def set_strokes(strokes)
+      @strokes = strokes
+      init
+    end
+    def draw
+      @strokes.strokes.each_with_index {|stroke, index|
+       draw_delay(stroke, index)
+      }
+    end
+    def draw_alpha(stroke, time)
+      px, py = 0, 0
+      span = 0.1
+      time += span*2
+      stroke.points.each {|x, y|
+       a = time / span
+       colorHSV(@hsv[0], @hsv[1], @hsv[2], a*100.0)
+       line(px, py, x, y) if (px != 0 || py != 0) #最初の点ではない
+       px, py = x, y
+       time -= span
+      }
+    end
+    def draw_delay(stroke, index)
+      now = Time.now
+      @start_time = Time.now if @start_time == nil
+      diff = now - @start_time #開始からの秒数がはいる
+      draw_alpha(stroke, diff - index*0.3)
+    end
+  end
+
+  class Stroke #====================================================================== 一本の線
+    def initialize
+      @points = []
+      @length = nil
+    end
+    attr_reader :points
+    def add_point(x, y)
+      @points << [x, y]
+    end
+    def length #未チェック
+      return @length if @length
+      len = 0.0
+      px, py = -1, -1
+      @points.each {|x, y|
+       if px != -1
+         len += Math.sqrt((x-px)*(x-px)+(y-py)*(y-py))
+       end
+       px, py = x, y
+      }
+      @length = len
+      return @length
+    end
+  end
+
+  class Strokes #====================================================================== 複数の線
+    def initialize
+      @strokes = []
+      @px1, @py1, @px2, @py2 = 0, 0, 0, 0
+      @x1, @y1, @x2, @y2 = 0, 0, 0, 0
+      @px, @py = -1, -1
+    end
+    attr_reader :strokes
+    def add_line(x1, y1, x2, y2)
+      if (@px != x1 || @py != y1) #以前の点とつながっていなかったら、
+       @strokes << Stroke.new
+       @strokes.last.add_point(x1, y1)
+      end
+      @strokes.last.add_point(x2, y2)
+      @px, @py = x2, y2
+    end
+  end
+
+  class CodeSelector #======================================================================
+    WIDTH, HEIGHT = 256, 256
+    SCALE = 2
+    def initialize(cx=0, cy=0)
+      @cx, @cy = cx, cy
+      @s = SCALE
+      @x1, @y1 = @cx-@s*WIDTH/2, @cy-@s*HEIGHT/2
+      @x2, @y2 = @cx+@s*WIDTH/2, @cy+@s*HEIGHT/2
+      @px, @py = @cx, @cy #とりあえず中心が開始点
+      @pw, @ph, @pr = 30, 30, 10
+      @dragging = false
+      @onkeydown = false
+      @code = 0
+      calc_code
+    end
+    attr_reader :code
+    def draw
+      colorHSV(0, 0, 100, 10) #まずは下敷きになる枠を書きます。
+      rect(@x1, @y1, @x2, @y2)
+      lineWidth(1)
+      colorHSV(0, 0, 100, 50)
+      b = 8; s = @s*WIDTH/b
+      (0..b).each {|n|
+       line(@x1, @y1+n*s, @x2, @y1+n*s)
+       line(@x1+n*s, @y1, @x1+n*s, @y2)
+      }
+      colorHSV(0, 100, 100, 100) # 次にポインターを書きます
+      circle(@px, @py, @pr)
+      line(@px-@pw/2, @py, @px+@pw/2, @py)
+      line(@px, @py-@ph/2, @px, @py+@ph/2)
+    end
+    def onMouse(x, y)
+      if @onkeydown
+       x, y = @px, @py
+      end
+      if @dragging || @onkeydown
+       @onkeydown = false
+       @px, @py = x, y      #p [x, y]
+       @px = @x1 if @px < @x1
+       @py = @y1 if @py < @y1
+       @px = @x2-1 if @x2-1 < @px
+       @py = @y2-1 if @y2-1 < @py
+       return calc_code
+      else
+       return false
+      end
+    end
+    def calc_code()
+      x = ((@px - @x1)/@s).to_i
+      y = HEIGHT-1 - ((@py - @y1)/@s).to_i
+      code = x + y*WIDTH
+      if @code != code
+       @code = code
+       return true #changed
+       #p [x, y, code]
+       printf("%02x %02x %04x\n", x, y, @code)
+      else
+       return false
+      end
+    end
+    def show_list(list)
+      colorHSV(0, 100, 100, 100)
+      list.each {|code|
+       x, y = code_to_xy(code)
+       rect(x, y, x+2, y-2)
+      }
+    end
+    def code_to_xy(code)
+      cx = code % WIDTH
+      cy = HEIGHT - (code / WIDTH) #intになる?
+      x = cx * SCALE + @x1
+      y = cy * SCALE + @y1
+      return x, y
+    end
+    def length(x, y)    Math.sqrt(x*x + y*y)  end
+    def onMouseDown(x, y)
+      if length(@px-x, @py-y) < @pr
+       @dragging = true
+      end
+    end
+    def onMouseUp(x, y)    @dragging = false  end
+    def onKeyDown(key)
+      @onkeydown = true
+      case key
+      when 273
+       @py += @s
+      when 274
+       @py -= @s
+      when 276
+       @px -= @s
+      when 275
+       @px += @s
+      end
+    end
+  end
+
+end
+
+if $0 == __FILE__ #======================================================================
+    $LOAD_PATH << '../ruby/src'
+  require 'chise'
+  include CHISE
+  require 'stroke'
+  include StrokeFont
+
+  def setup
+    useSmooth()
+    window(-300,-300,300,300)
+    background 0,0,20
+    useFramerate(30)
+    @cs = CodeSelector.new
+    @csf1 = CSFFont.new()  #普通の文字
+    @csf2 = CSFFont.new(CSF_KOUKOTSU_FILE) #甲骨文字
+    @key = 1
+    @kage = KageFont.new()
+    @changed = nil
+  end
+
+  def display
+    @changed = @cs.onMouse(mouseX, mouseY) #変化があったか?
+    @cs.draw
+    @cs.show_list(@kage.cache_list)
+    code = @cs.code
+
+    push
+    scale 0.2
+    translate -500,-500
+    lineWidth(2)
+    draw_kage(code)
+    draw_csf(code)
+    pop
+  end
+
+  def draw_kage(code)
+    char = Character.get(code)
+    return if char.nil?
+    @kage.init(code) if @changed
+    @kage.print(code) if @changed
+    @kage.draw(code)
+  end
+
+  def draw_csf(ucs)
+    char = Character.get(ucs)
+    return if char.nil?
+    j = char.japanese_jisx0208
+    return if j.nil?
+    code = j
+    csf = @key == 1 ? @csf1 : @csf2
+    csf.init(code) if @changed
+    csf.print(code) if @changed
+    csf.draw(code)
+  end
+
+  def onMouseDown(x, y)  @cs.onMouseDown(x, y)end
+  def onMouseUp(x, y)  @cs.onMouseUp(x, y)end
+  def onKeyDown(key)
+    @key = key
+    @cs.onKeyDown(key)
+  end
+  mainloop
+end
+
+#----------------------------------------------------------------------end.