テキストでプレゼンテーションツールkoxela
読みは"こけら"で、お願いします。
で、例のごとくPartty!.orgで録画しようとしたのですが、Partty!.orgのターミナルエミュレータは256色エスケープシーケンス対応じゃないというアレなんで、Partty!.org付属のcapttyを使います。
実はPartty!.orgはURLの"session"のところを"pty"に書き換えると、capttyで再生できる形式のファイルが落とせるのです。
と、いうことで、まず256色対応のターミナルを用意します。
256対応かどうかはここサイトでチェックできます。
で、Partty!.orgからcapttyデータを落としてきます。
http://www.partty.org/pty/goth/2008/05/03/01/28/36
あとはcapttyで再生するだけ
$ captty play 36
ファイル名は適当に。
一応ガジェットも張っておきますが、最後の最後で盛大にバグります。
はやく256色対応しないかなぁ〜 >> id:viver
今回の録画したターミナルがバカでかいので、フォントサイズを下げるなりしてpartty-scaleがちゃんと表示されるようにしてください。
録画データの先頭の方でpartty-scaleを何度か叩いてますが、合わせられなかったらgを押してもう一度先頭にrewindして何度も調整してやってください。
サイズは214x62です。
と、いうことで、ここからkoxelaの説明。
koxelaは完全テキストベースのプレゼンテーションツールです。
プレゼンテーションデータはYAML形式で書かれてます。
vimさえあれば簡単にプレゼンが作れます。
自前でパース、も考えたのですが、まぁ、YAMLでいいでしょう。
実際のプレゼンテーションデータ(の一部)です。(拡張子はなんとなく.kxl
# filename: demo.kxl # vim: ft=yaml:sw=2:ts=2:sts=2:et common: width: 600 height: 400 background: [0, 0, 0] foreground: [1, 1, 1] chars: " .,~=+?I7Z08DNM#" font: name: nil size: 200 line: width: 10 pages: - title: Top objects: - type: "string" string: "自己紹介" pos: [300, 200] size: 150 - title: Intro0 # 略
インデントが意味を持つので注意。あとインデントは半角スペースのみで、Tabは不可です。
あと"#"から始まる行はコメントです。
まぁ、その辺はYAMLの仕様で。
で。最初のcommon。
width/heightはkxlファイルの中で使う仮想的な座標系の大きさを表します。
実際にはターミナルのサイズにスケールされます。
と、いうのも、ターミナルサイズはいつも固定ワケではないので、kxlファイルの中で"X文字目のY列目"とか指定してしまうと、ターミナルサイズ変更で困ることに...
もっと大きな値を与えてもいいのでしょうけど、まぁ、どうせターミナルサイズにスケールされるので...。
background/foregroundはプレゼンの背景色とオブジェクトのデフォの描画色です。
後術するobjectにcolorを指定しない場合foregroundが適応されます。
個人的に黒背景のターミナルが基本なんで、背景は黒、描画色が白になってます。
charsはアスキーアート化するときに使う文字。
右に行くほど線密度が濃くなるようにするとよさげ。" #"で二階調になります。
fontはstring objectのデフォのフォント。
nameをnilにすると適当に並んでくれるそうです(Cairoに丸投げなもので、その辺はRubyCairoの仕様です...。
lineのwidthはデフォの線の太さ。
この辺はまだ適当に与えてるのでガラッと仕様変えるかも...(д
pagesのところから実際のプレゼンデータが始まります。
- title: Top objects: - type: "string" string: "自己紹介" pos: [300, 200] size: 150
titleはページのタイトル...なんだけど何にも使ってないというね。
ターミナルのタイトルとか変えようかなとか画策ちぅ。
typeは"string"の他"line","rect","circle"を実装済み。
stringは文字を描画。
必須なプロパティは、表示する文字を指定する"string"と、表示する座標"pos"
オプショナルで"font", "size", "color", "width"(線の太さ), "method"("fill"で塗りつぶし、"stroke"で輪郭だけ)
lineは線を描画。必須は、始点/終点座標"pos1"、"pos2"
オプショナルで"color", "width"
rectは四角を描画。必須は、角の2点の座標"rect"([x1, y1, x2, y2])
オプショナルで"color", "width"
circleは円。必須は中心座標"pos"と半径"radius"
オプショナルで"color", "width"
と、言った感じ。
細かい修正(エラーチェックとか仕様の策定とか)したら正式にリリースしたいと思います〜。(とか言って忘れるんだよな...。atode "koxelaをリリースする"
とりあえず現段階のソースコード
#!/usr/bin/env ruby # lisence = とりあえずGPLv2 require "cairo" require "yaml" TIOCGWINSZ = 0x00005413 TIOCSWINSZ = 0x00005414 str = "" $stdin.ioctl( TIOCGWINSZ, str ) (row, col) = str.unpack("S!4") if ARGF.filename == "-" then exit end yaml = YAML.load( ARGF.read ) width = yaml["common"]["width"] || 600 height = yaml["common"]["height"] || 400 bg = yaml["common"]["background"] || [0, 0, 0] fg = yaml["common"]["foreground"] || [1, 1, 1] chars = yaml["common"]["chars"] || " .,~=+?I7Z08DNM#" fontName = yaml["common"]["font"]["name"] || "" fontSize = yaml["common"]["font"]["size"] || 400 lineWidth = yaml["common"]["line"]["width"] || 10 pages = yaml["pages"] || [] surface = Cairo::ImageSurface.new( col, row ) context = Cairo::Context.new( surface ) context.scale( col / width.to_f, row / height.to_f ) basename = File.basename ARGF.filename, ".*" File.open( "#{basename}.sh", "w" ){ |fp| fp.puts "#!/bin/sh" fp.puts "# col:#{col} row:#{row}" pages.each_with_index{ |page,i| context.set_source_rgb( bg ).rectangle( 0, 0, width, height ).fill context.save page["objects"].each{ |obj| case obj["type"] when "line" context.set_source_rgb( obj["color"] || fg ) context.move_to( obj["pos1"][0], obj["pos1"][1] ) context.line_to( obj["pos2"][0], obj["pos2"][1] ) context.set_line_width( obj["width"] || lineWidth ) case obj["method"] when "fill" context.fill when "stroke" context.stroke else context.stroke end when "circle" context.set_source_rgb( obj["color"] || fg ) context.circle( obj["pos"][0], obj["pos"][1], obj["radius"] ) context.set_line_width( obj["width"] || lineWidth ) case obj["method"] when "fill" context.fill when "stroke" context.stroke else context.fill end when "rect" context.set_source_rgb( obj["color"] || fg ) context.rectangle( *obj["rect"] ) context.set_line_width( obj["width"] || lineWidth ) case obj["method"] when "fill" context.fill when "stroke" context.stroke else context.stroke end when "string" context.select_font_face( obj["font"] || fontName, 0, 0 ) context.set_font_size( obj["size"] || fontSize ) context.set_source_rgb( obj["color"] || fg ) context.set_line_width( obj["width"] || lineWidth ) ext = context.text_extents( obj["string"] ) extWidth = ext.x_advance - ext.x_bearing extHeight = ext.y_advance - ext.y_bearing context.move_to( obj["pos"][0] - extWidth / 2, obj["pos"][1] + extHeight / 2) context.text_path( obj["string"] ) case obj["method"] when "fill" context.fill when "stroke" context.stroke else context.fill end else end } preColorCode = 0 jabanner = "" row.times { |y| col.times { |x| rr = surface.data[4*(x + y * col) + 2] gg = surface.data[4*(x + y * col) + 1] bb = surface.data[4*(x + y * col) + 0] begin colorCode = (rr * 6 / 256) * 36 + (gg * 6 / 256) * 6 + (bb * 6 / 256) + 16 if colorCode != preColorCode then jabanner += "\033[38;5;%dm" % colorCode preColorCode = colorCode end end yy = (rr * 0.299 + gg * 0.587 + bb * 0.114) jabanner += chars[chars.length * yy / 256].chr } } fp.puts "echo\ \"#{jabanner.gsub( "\"", "\\\"" )}\"" fp.puts "read" context.restore } surface.finish fp.puts "reset" } `chmod u+x #{basename}.sh`
きたねぇソース...
激氣
オープンソースなんで適当にいじっちゃってくださいな。
難しいことは何一つしてないので、多分人によっては10分コースぐらいかと。