ソフトウェア/pukiwiki/tchart.inc.php のバックアップ差分(No.2)

更新


  • 追加された行はこの色です。
  • 削除された行はこの色です。
[[公開メモ]]

* タイミング図を書きたい [#q244f333]
#contents

* pukiwiki でタイミング図を書きたい [#q244f333]

デジタル信号処理の記事を書くのに、

&tchart(
clock	_~_~_~_~_~_~_~_~_~_~_
data	=====X=DATA========X=====
valid	_____~~~~~~~~~~______
ready	_____________~~______
);

こういったタイミング図を書きたくて、
プラグインにしてみました。

* tchart [#c7bb7415]

非常に便利なツールを熊谷正朗さんが公開しておられます。

- タイミングチャート清書ツール tchart~
http://www.mech.tohoku-gakuin.ac.jp/rde/contents/library/tchart/indexframe.html

というツールを熊谷正朗さんが公開しておられます。

このツールを使うと、

 clock	_~_~_~_~_~_~_~_~_~_~_
 data	=====X=DATA========X=====
 valid	_____~~~~~~~~~~______
 ready	_____________~~______

こんな風にテキストで書いたタイミング図を、

&tchart(
clock	_~_~_~_~_~_~_~_~_~_~_
data	=====X=DATA========X=====
valid	_____~~~~~~~~~~______
ready	_____________~~______
);

このように図にすることができます。
このように画像化できます。

ツール自体はテキストからポストスクリプト(eps)を出力するのですが、
そのままだとホームページに表示できないので、svg に変換するコードを別途書いています。
ツール自体の出力はポストスクリプトになるのですが、
そのままだとホームページ上に表示できません。
本当なら tchart 自体を svg を出力する形に手直しをすれば良いのですが、
ここでは手抜きをして、ポストスクリプトファイルを svg 
に変換するコードを別途書きました。

* svg ファイルを作るまでの処理 [#l0035e32]

gohstscript と pdf2svg がインストールされていれば、
以下のようにしてポストスクリプトから SVG を作成できます。

 LANG:console
 $ tchart chart.tc chart.eps
 $ ps2pdf chart.eps chart.pdf
 $ pdf2svg chart.pdf chart.svg

* 処理の流れ [#i7a809f2]

+ pukiwiki.php を変更して tchart プラグインに複数行にわたる引数を入力できるようにする
+ 外部ツール tchart を呼び出して eps を作成
+ pukiwiki.php を変更して tchart プラグインに複数行にわたる引数を与えられるようにする
+ tchart を呼び出して eps を作成
+ ps2pdf で pdf に変換 https://packages.debian.org/cgi-bin/search_contents.pl?word=ps2pdf
+ pdf2svg で svg に変換 https://packages.debian.org/search?keywords=pdf2svg
+ ruby で書いたプログラムで svg の余白をトリミングする
+ 変換結果の svg をファイルのキャッシュする
+ html に直接埋め込んで表示する
+ 変換結果の svg は次回の表示のためにキャッシュする

* svg ファイルを作るまでの処理 [#l0035e32]

 $ tchart chart.tc chart.eps
 $ ps2pdf chart.eps chart.pdf
 $ pdf2svg chart.pdf chart.svg

* tchart ソースから svg を作り、余白をトリミングするスクリプト [#s5d23c0e]

本当なら tchart 自体を svg を出力する形に手直しをすれば良いのですが、
ここでは手抜きをしています。
ruby で書きました。tchart ソースを与えると標準出力に SVG コードを出力します。

/usr/local/bin/tchart-svg.rb
 LANG:ruby(linenumber)
 #!/usr/bin/ruby
 
 require "digest/md5"
 require "tempfile"
 
 ##################################################### SVG を作る
 
 source = readlines.join('')
 
 ##################################################### SVG を作る
  
 file = ''
 temp = Tempfile.open(['tchart', '.tc'])
 file = temp.path.sub(/\..+/, '')
 temp.write source+"\n"
 temp.close
 
 system("/usr/local/bin/tchart.pl #{file}.tc #{file}.eps") or exit
 system("/usr/bin/ps2pdf #{file}.eps #{file}.pdf") or exit
 system("/usr/bin/pdf2svg #{file}.pdf #{file}.svg") or exit
 
 lines = ''
 open("#{file}.svg") do |svg|
   lines = svg.readlines
 end
 
 system("/bin/rm #{file}.tc #{file}.eps #{file}.pdf #{file}.svg")
 
 ##################################################### トリミングする
 
 surface = lines.find_index {|line| line =~ /<g id="surface1"/ }
 exit -1 unless surface
 
 exit -1 unless lines[1] =~ /viewBox="0 0 (\d+) (\d+)"/
 viewx = $1.to_f
 viewy = $2.to_f
 
 exit -1 unless lines[surface+1] =~ /d="M (\d+) (\d+) L (\d+) (\d+) L (\d+) (\d+) /
 offsetx = 0.1 * $1.to_f
 offsety = 0.1 * $2.to_f
 boundx = 0.1 * $5.to_f - offsetx
 boundy = - 0.1 * $6.to_f + offsety
 
 lines.delete_at(surface+1)
 lines[surface] = "<g id=\"surface1\" transform=\"matrix(1,0,0,1,#{-offsetx},#{offsety-viewy})\">\n"
 lines[1].sub!(/(?<=width=")\d+(?=pt")/, boundx.to_s)
 lines[1].sub!(/(?<=height=")\d+(?=pt")/, boundy.to_s)
 lines[1].sub!(/(?<=viewBox\="0 0 )\d+ \d+(?=")/, "#{boundx} #{boundy}")
 
 ##################################################### グリフ名を変える
 
 svg = lines.join('')
 key = Digest::MD5.hexdigest(svg)[0,6]
 
 ##################################################### 出力する
 
 print svg.gsub(/\bglyph/, key)

~
処理内容は以下のようになっています:

- 標準入力へ渡された tchart のソースコードをテンポラリファイルに書き出し、
外部ツールを使って svg に変換する
- 作成された svg には A4 いっぱいの余白が付いてしまうので、
四角で囲まれた範囲外の余白を取り除く
- 1つの HTML に複数の SVG を貼るとき、グリフ名が重複すると結果がおかしくなってしまうため、
SVG 中のグリフ名を他と重ならないように付け替えます

* pukiwiki.php の変更 [#a8322c5d]

標準ではプラグインに渡す引数に改行文字を入れられないので、
tchart プラグインに限って改行入りの引数を渡せるように変更します。

具体的には、改行文字を <br> という文字に変更しておき、
プラグイン側で改行文字に復元します。

 LANG:php
 //       $body  = get_source($base);
 
        $lines = get_source($base);
        for($i=0;$i<count($lines);$i++)
            if (preg_match('/\&tchart\(/', $lines[$i]) ) 
                while(!preg_match('/\&tchart\(.*?\)\;/', $lines[$i]) && ($i+1<count($lines)))
                    array_splice($lines, $i, 2, chop($lines[$i]) . '<br>' . $lines[$i+1] );
        $body  = convert_html($lines);

* plugin/tchart.inc.php [#ge40e5b5]

- 引数を連結して1つの文字列にする
- <br> を改行に戻す
- キャッシュされたファイルが無いか探す
-- 無ければソースを tchart-svg.rb へ渡して結果をキャッシュする
-- あればキャッシュを読み出す
- そのまま SVG コードを返す

 LANG:php(linenumber)
 <?php
 
 // 画像ファイルのキャッシュ場所
 define('TCHART_DIR', 'tchart');
 
 function tchart($source)
 {
     $descriptorspec = array(
        0 => array("pipe", "r"),             // stdin
        1 => array("pipe", "w"),             // stdout
        2 => array("file", "/dev/null", "a") // stderr
     );
     $process = proc_open('/usr/local/bin/tchart-svg.rb', $descriptorspec, $pipes);
 
     if (is_resource($process)) {
         fwrite($pipes[0], $source);
         fclose($pipes[0]);
 
         $result = stream_get_contents($pipes[1]);
         fclose($pipes[1]);
 
         proc_close($process);
     }
     return $result;
 }
 
 function tchart_cached($source)
 {
     $result = '';
 
     $image_base = sha1($source);
     $image_dir = TCHART_DIR . "/{$image_base[0]}/{$image_base[1]}";
     $image_svg = "{$image_dir}/{$image_base}.svg";
 
     if(!file_exists($image_svg)) {
         $svg = tchart($source);
         
         if (mb_substr($svg[0],0,4)=="<svg") {
             if(!file_exists($image_dir)){
                 mkdir($image_dir, 0770, true);
             }
             $fh = fopen($image_svg, "w");
             fwrite($fh, join("", $svg));
             fclose($fh);
         }
     } else {
         $svg = file($image_svg);
     }
 
     return $svg;
 }
 
 function plugin_tchart_inline()
 {
     $args = func_get_args();
     $source = join(",", $args);
     $source = preg_replace('/<br>/', "\n", $source);
     return tchart_cached($source);
 }
 
 ?>

* コメント・質問 [#f37cb237]

#article_kcaptcha


Counter: 3735 (from 2010/06/03), today: 1, yesterday: 0