Pages

1/19/2011

Mapping Character Between Thai Keyboard Layout To English Keyboard layout

ต่อจาก โพสต์ที่แล้ว http://blog.anidear.com/2011/01/starting-wordlist-for-thai-language.html

ตอนนี้ก็เริ่มมาหาว่า จะทำยังไงให้ map ระหว่าง keyboard layout ภาษาไทยไปอังกฤษได้ (หรือจากอังกฤษมาไทยได้)
วิธีที่basicสุดๆเลยก็คือ นั่งกด ทีละตัวๆ ระหว่างไทยและอังกฤษ เพื่อทำเป็นตารางขึ้นมา

อืม... ก็ไม่ได้ผิดอะไรนะวิธีนี้ แต่ดูมันถึกเกินไปหน่อย เพราะนอกจากจะต้องไล่กดทีละปุ่มบนkeyboardแล้ว ยังต้องมาไล่อีกรอบนึง เพราะว่าตัวหนังสือก็มีแบบกดshiftค้างไว้ด้วย เหนื่อยแน่ แล้วก็ดูไม่ค่อยมาตรฐานเท่าไหร่เพราะอาจจะมีkeyอะไรลืมกดก็ได้

ก็เลยคิดอีกวิธีขึ้นมา ว่ามันน่าจะมีระบบ keyboard layout อยู่แล้วนะ
ยิ่งใน Linux ทุกอย่างแทบจะเป็นtext file หมด น่าจะเอามาทำอะไรได้
พอค้นไปค้นมา ก็ไปเจอไฟล์หลักๆ มาสามไฟล์
http://cvsweb.xfree86.org/cvsweb/*checkout*/xc/programs/xkbcomp/symbols/th?rev=HEAD&content-type=text/plain
http://cvsweb.xfree86.org/cvsweb/*checkout*/xc/programs/xkbcomp/symbols/us?rev=HEAD&content-type=text/plain
http://cvsweb.xfree86.org/cvsweb/*checkout*/xc/include/keysymdef.h?rev=1.1.1.4

สามไฟล์นี้คือ th layout, en-us layout, symbol โดยทั้งหมดมาจากpackageที่ชื่อ xc ของ XFree86 ทั้งหมด
คราวนี้ก็เริ่มง่ายละ เพราะว่าข้อมูลต่างๆ ก็พร้อมละ
th layout นี่ก็คือปุ่มกดทุกปุ่มบน keyboard ในภาษาไทย
en-us layout ก็คือปุ่มกดทุกปุ่มบน keyboard ภาษาอังกฤษ
ส่วน keysymdef นี่คือการแปลงจากค่าสัญลักษณ์ของปุ่มกดนั้น ให้กลายเป็นตัวอักษร อย่างเช่น XK_Thai_kokai---> 0xda1 (จาก ก.ไก่ --> 0xda1)

คราวนี้ก็จะเห็นว่าข้อมูลในไฟล์มันรกมาก ในสองไฟล์แรก
ก็เลยจัดการทำความสะอาดซักหน่อย (ด้วย find&replace)
เลยได้เป็นไฟล์ประมาณนี้
จะเห็นว่าเป็น 3 columns โดยมีความหมายดังนี้
อันแรก : keyboard code (keycode) คือรหัสสัญญาณที่keyboardส่งไปให้คอมพิวเตอร์รับรู้ว่าเรากดปุ่มอะไร
อันสอง : ตัวอักษร แบบไม่ได้กดshift
อันที่สาม: ตัวอักษร แบบกดshiftควบคู่ไปด้วย

เช่น บรรทัดที่สาม  AE02  slash  Thai_leknung หมายความว่า
ถ้ากดปุ่ม AE02 แล้ว  กดเฉยๆเลยจะได้เป็น  slash (/)
แต่ถ้ากดปุ่ม shift ค้างไว้แล้วกดปุ่ม AE02  จะได้ออกมาเป็น Thai_leknung (๑)

งานที่เหลือก็คือ  อย่างแรก map keycode นี้เข้าด้วยกัน ระหว่าง ภาษาไทยและภาษาอังกฤษ
อย่างที่สองก็คือ ต้องแปลง symbol ทั้งหลายนี้ ให้มันกลายเป็นตัวอักษรปกติ ด้วย

อ้อ ตัวอักษรอย่าง 0xda1 ที่ว่าเป็นก.ไก่เนี่ย ความจริงต้องตัด d ออกก่อน เหลือจริงๆแค่ 0xa1 ถึงจะเป็น ก.ไก่ จริงๆ ในระบบ tis-620    นั่นก็ต้องเปลี่ยนให้เป็น utf-8 เอาไว้ใช้งานอย่างอื่นอีก

ซึ่ง... ถ้าจะทำแบบธรรมดาก็จะเหนื่อยไปอีกแล้ว  เห็นแล้วมันก็เยอะ  เขียนโปรแกรมดีกว่า
แล้วไหนๆ ก็กำลังอยากเรียน Ruby อยู่แล้วด้วย ก็เขียนซะเลย

#!/usr/bin/ruby

require 'iconv'
require 'optparse'


#read keyboard-char map
def readFile(filename)
 mappingtable={}
 f = File.open(filename, "r")
 f.each_line do |line|
  a=line.split 
  mappingtable[a[0]] = [ a[1],a[2] ]
 end
 return mappingtable
end

#read symbol char-keycode map
def readKeyTranslate(filename)
 mappingtable={}
 f = File.open(filename, "r")
 f.each_line do |line|
  if(line=~/^[#]define/)
   a=line.split 
   mappingtable.store(a[1],a[2])
  end
 end
 return mappingtable
end

#convert from hex to tis620 then to utf8
def convHexToChar(hexchar)
 return nil if hexchar.nil? 
 hexchar.gsub!(/0x./,'')
 char=hexchar.to_i(16).chr
 char = Iconv.conv("UTF8","TIS620", char)
 return char
end


#dealing with program's arguments

$verbose, $thfile, $usfile, $keysymfile = false, 'th.txt', 'us.txt', 'keysymdef.h.txt'
options = {}
OptionParser.new do |opts|
 opts.banner="Usage: switchlayout.rb [options]"
 opts.on("-v", "--verbose", "Run verbosely") {|v| $verbose=options[:verbose]=v}
 opts.on("-u","--usfile [US_KB_FILE]",String,"Specify en-us keyboard file (default=us.txt)") {|us| $usfile=options[:us]=us}
 opts.on("-t","--thfile [THAI_KB_FILE]",String,"Specify Thai keyboard file (default=th.txt)") {|th| $thfile=options[:th] = th}
 opts.on("-k","--keysym [keysymdef.h_FILE]",String,"Specify keyboard symbol file (default=keysymdef.h.txt)") {|keysym| $keysymfile=options[:keysym]=keysym}
 opts.on_tail("-h","--help","Show this message") { puts opts ; exit}
end.parse!

puts "Starting process..."
#reading all neccessary files
thMap = readFile($thfile)
puts "Total #{thMap.size} symbols loaded for Thai Mapping" if $verbose

usMap = readFile($usfile)
puts "Total #{usMap.size} symbols loaded for English(us) Mapping" if $verbose

keyCodeMap = readKeyTranslate($keysymfile)
puts "Total #{keyCodeMap.size} symbols loaded for KeyCode Mapping" if $verbose

#translate from thMap's key symbol to keycode
#note: pair[0]=keycode , pair[1]=[char w/o shift, char w/ shift]
thMap.each do |pair|
 print "#{pair[1]} ----> " if $verbose
 k = pair[1];
 k[0] = convHexToChar(keyCodeMap["XK_#{k[0]}"])
 k[1] = convHexToChar(keyCodeMap["XK_#{k[1]}"])
 puts "#{pair[1]}" if $verbose
end

#translate from usMap's key symbol to keycode
#note: pair[0]=keycode , pair[1]=[char w/o shift, char w/ shift]
usMap.each do |pair|
 print "#{pair[1]} ----> " if $verbose
 k = pair[1];
 k[0] = convHexToChar(keyCodeMap["XK_#{k[0]}"])
 k[1] = convHexToChar(keyCodeMap["XK_#{k[1]}"])
 puts "#{pair[1]}" if $verbose
end

#now mapping from enMap & thMap
$usTothMap = {}
usMap.merge(thMap) do |key, usChar, thChar|
 $usTothMap.store(usChar[0],thChar[0])
 $usTothMap.store(usChar[1],thChar[1])
end

$usTothMap.each {|us,th| puts "#{us} ---> #{th}"} if $verbose
puts "Total characters mapped = "+$usTothMap.size.to_s

#write to file
puts "Writing to file ustothmap.txt"
f = File.open('ustothmap.txt', 'w')
$usTothMap.each {|us,th| f.puts "#{us}\t#{th}"}
f.close
อ่านยากแฮะ แบบไม่มีสีเนี่ย

เสียเวลานั่งงมอยู่นานเหมือนกัน  กว่าจะรู้ว่าอะไรเป็นอะไรใน Ruby
และแล้ว ในที่สุด ก็ได้ออกมาเป็นไฟล์ usTothMap.txt
คราวนี้ก็จะได้ map ระหว่าง en-us กับ th ได้แล้ว

ทั้งหมดมีแค่ 94 ตัวอักษรเองแฮะ
บางทีเสียเวลานั่งพิมพ์เองจะง่ายกว่ามั๊ง  55555

No comments:

Post a Comment