方針 EditToHeaderToFooter

  • 基本仕様は wikihouse.commath2.inc.php に従う。
    • ただし、ハッシュの衝突が心配のため、数式の同定は生のTeXコードで比較。
    • プラグインの同時呼出しで作業ファイルが衝突するため、全てハッシュ名を用いる。
    • LaTeX、dvipngにより数式画像生成後に、Imagemagic の convert で画像処理を施す。
  • 数式の解像度、位置決めは、それなりに拘る。
    • 少なくとも IE7、IE8、Firefox3 の100% ズームを満足し、Firefox3 の最大ズームに耐えうること。

仕様 EditToHeaderToFooter

  • パスを通した platex、dvipng と convert を要求。
  • 引数をそのままLaTeXに渡し、最終的にpng画像に変換して出力。
    • ブロックプラグインの場合は、align環境(\begin(align) 〜 \end(align))に渡される。
    • インラインプラグインの場合は、ディスプレイ数式モードに変更されるテキスト数式モード($ \displaystyle 〜 $)に渡される。
  • 作業ファイルと出力ファイルの名前には、数式に基づくハッシュキーを使用。
    • eq/{ハッシュキー}.eq に生のTeX式を保存し、数式の同定に使用。ハッシュキーを同定には使わない。
    • eq/{ハッシュキー}.png に数式画像を出力。
  • ブラウザのズーム対策として、画像を高解像度で出力し、IMGタグのwidthheightで縮小表示。
  • インライン数式は垂直中心揃え(vertical-align: middle;)。分数線が中心線に重ねるようにTeX側で調節する。
    • dviには位置決めのために数式に枠を付けて出力。convert により枠を除去。
  • 数式がコンパイルできない場合、代わりに TeX のエラーを出力。

改造 EditToHeaderToFooter

  • plugin/eq.inc.php を作成して、
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    
    <?php
     
    function plugin_eq_convert()
    {
        $arg  = rtrim(join(func_get_args(), ','), ',');   // 末尾の「,」は波引数が空の場合に現れ、不要物。
        return '<div class="eq">'.plugin_eq_core($arg, 'convert').'</div>';
    }
     
    function plugin_eq_inline()
    {
        $arg  = rtrim(join(func_get_args(), ','), ',');   // 末尾の「,」は波引数が空の場合に現れ、不要物。
        return '<span class="eq">'.plugin_eq_core($arg, 'inline').'</span>';
    }
     
    function plugin_eq_core($arg, $mode)
    {
        $eq_dir = './eq/';                                      // 数式と数式画像のパス
        $err_msg_end_phrase = 'No pages of output.'.PHP_EOL;    // エラー終了を示す文字列(platexの出力依存)
        
        $prefix_convert = 'eq-nc-';             //  ブロック用ファイル衝突を避けるための接頭語
        $prefix_inline  = 'eq-ni-';             // インライン用ファイル衝突を避けるための接頭語
        $tex_frame_convert  = 'convert.tex';    //  ブロック用フレームファイル名
        $tex_frame_inline   = 'inline.tex' ;    // インライン用フレームファイル名
        $eq_replace_phrase  = '\input{eq}' ;    // フレームファイルで数式を埋め込む部を示す文字列
        
        $key = ($mode=='inline') ? $prefix_inline.md5($arg) : $prefix_convert.md5($arg);
        
        $eq  = $key.'.eq';      // 照合用引数出力(保存ファイル)
        $img = $key.'.png';     // 数式の画像出力(保存ファイル)
        
        $old_dir = getcwd();
        chdir($eq_dir);
        
        if (!file_exists($img) || $arg != file_get_contents($eq))
        {
            // 一時ファイル
            $tex  = $key.'.tex';
            $std  = $key.'.std.log';   // Tex の標準出力、エラー出力用
            $dvi  = $key.'.dvi';
            $aux  = $key.'.aux';
            $log  = $key.'.log';
            $box  = $key.'.box.png';
            $face = $key.'.face.png';
            $edge = $key.'.edge.png';
            $bold = $key.'.bold.png';
            
            flush();    // Timeout 対策
            $tex_code  = file_get_contents(($mode=='inline') ? $tex_frame_inline : $tex_frame_convert);
            $tex_code  = str_replace($eq_replace_phrase, $arg, $tex_code);
            
            file_put_contents($eq  ,$arg     );
            file_put_contents($tex ,$tex_code);
            exec('platex -halt-on-error '.$tex.' > '.$std);
            
            flush();    // Timeout 対策
            if (!file_exists($dvi)) // Tex Error
            {
                $err_flag = false;
                $err_code = "Tex Error:\n\n";
                
                if ($log_code = fopen($std, 'r'))
                {
                    while (!feof($log_code)) 
                    {
                        $tex_msg = fgets($log_code);
                        
                             if ($tex_msg[0] == '!'                ) {$err_flag = true ;}
                        else if ($tex_msg    == $err_msg_end_phrase) {$err_flag = false;}
                        
                        if ($err_flag) {$err_code .= $tex_msg;}
                    }
                    fclose($log_code);
                }
                chdir($old_dir);
                return '<pre class=eq_error_title>'.$arg.'</pre><pre class=eq_error_message>'.$err_code.'</pre>';
            }
            exec('dvipng -D 720 -T tight --gamma 2.5 -o '.$box.' '.$dvi);
            if ($mode=='inline') {
                        list($width, $height) = getimagesize($box);
                        exec('convert '.$box.' -crop '.($width-16).'x'.($height-16).'+8+8 -bordercolor #FFFFFF               -threshold 50% '.$face);
            } else {
                        exec('convert '.$box.'                                            -bordercolor #FFFFFF -border 16x16 -threshold 50% '.$face);
            }
            exec('convert '.$face.' -edge 1 -negate '.$edge);
            exec('composite -compose multiply '.$face.' '.$edge.' '.$bold);
            exec('convert '.$bold.' -black-threshold 90% -antialias -blur 1 - | convert - -transparent #FFFFFF -antialias '.$img);
            
            flush();    // Timeout 対策
            unlink($tex );
            unlink($std );
            unlink($dvi );
            unlink($aux );
            unlink($log );
            unlink($box );
            unlink($face);
            unlink($edge);
            unlink($bold);
        }
        list($width, $height) = getimagesize($img);
        $zoom = 0.01;
        $width  *= $zoom;
        $height *= $zoom;
        $depth   = 0.35 - $height/2;  // unit:em
        chdir($old_dir);
        return '<img style="width:'.$width.'em; height:'.$height.'em; vertical-align:'.$depth.'em;". src="'.$eq_dir.$img.'" alt="eq: '.$arg.'" title="'.$arg.'">';
    }
    ?>
    • 17行目は数式のパスを指定。数式のキャッシュとみなせるため、cache/と同じレベルに eq/ を作った。
    • 26行目は数式のハッシュキー。プラグインの種別と数式の md5 ハッシュに依存。
    • 34〜98行目は変換処理。数式が変換済みの場合は分岐が成立せず、処理が省かれる。
    • 47行目、55行目と88行目の flash(); はタイムアウト対策。これらが無いと、大量の数式を変換すると PHP が落ちる。
    • 48、49行目は TeX コードの雛形の読み込みと数式の組み込み(=置換)。52行目で数式の TeX ファイルを出力。
    • 56〜76行目は TeX がエラーのときの処理。エラーの場合、ログを解析し、エラーの部分をだけ出力。
      • 67行目と68行目はそれぞれエラー部の開始と終了の検出。これが環境依存のため、変更が必要かもしれない。
    • 77〜86行目は画像処理。インラインプラグインでは位置決め枠の除去のため、ブロックプラグインより処理が多い。
    • 99行目、102行目は数式画像のサイズを取得し、表示サイズを算出して出力している。
      • 数式画像の解像度は、77行目の dvipng の解像度 -D 720 で設定。
      • 数式画像の表示サイズは、解像度 と100行目の $zoom に依存。
  • eq/convert.tex を作成して、
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
    
    \documentclass[12pt]{jarticle}
    \usepackage{amsmath,amssymb,bm}
    \usepackage{pukiwiki}
    \pagestyle{empty}
     
    \begin{document}
    \thispagestyle{empty}
    \begin{align*}
    \input{eq}
    \end{align*}
    \end{document}
    • \input{eq} をプラグインに渡されるTeXコードで置換して使用。
  • eq/inline.tex を作成して、
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
    
    \documentclass[12pt]{jarticle}
    \usepackage{amsmath,amssymb,bm}
    \usepackage{pukiwiki}
    \pagestyle{empty}
     
    \makeatletter
    \newcommand\inlineframe[1]%
    {%
        \setbox0=\hbox{\mathstrut}%     基字「(」相当
        \setbox1=\hbox{#1}%             対象
        \dimen0=\ht0    \advance \dimen0 by -\dp0
        \dimen1=\dp1    \advance \dimen1 by  \dimen0
        \dimen2=\ht1    \advance \dimen2 by -\dimen0
        \fbox{%
            \rule{0pt}{\dimen1}%
            {#1}%
            \rule[-\dimen2]{0pt}{\dimen2}%
        }%
    }%
    \makeatother
     
    \begin{document}
    \thispagestyle{empty}
    \inlineframe{$ \displaystyle \mathstrut {
    \input{eq}
    } $}
    \end{document}
    • 6〜20行目で定義した \eqframe は位置決めするための枠を付けるコマンド。
    • \input{eq} をプラグインに渡されるTeXコードで置換して使用。

参考:柱の算出 EditToHeaderToFooter

TexEqMiddleAlign.png

  • TeXでは、「(」に相当する \mathstruct の中心を通る線が分数線などに一致。以下、これを「中線」と呼ぶ。
  • 数式画像の範囲は赤線が示すようになっており、中線が図の中心を通る保障は無い。数式は中線で揃えるため、中線が図の中央を通ると都合が良い。(要検討)
  • 中線を図の中心に合わせるため、柱$$ H_m $$$$ D_m $$が必要。
    • $$ H_m = -d_m $$は、$$ \ffd{x}{\ffd{x}{x}} $$のように、中線基準で下辺が離れている場合に、中線が図の中心になるよう上辺を遠ざける。
    • $$ D_m = -h_m $$は、$$ \ffd{\ffd{x}{x}}{x} $$のように、中線基準で上辺が離れている場合に、中線が図の中心になるよう下辺を遠ざける。
  • 実際はTeXは基線(ベースライン)基準のため、換算した柱$$ H $$$$ D $$を使う。
  • 高度$$ h_0 $$と深度$$ d_0 $$\mathstrut の中心を中線が通るため、基線基準の中線位置は$$ m=\ffd{h_0 + d_0}{2} $$と求まる。~ここで、\mathstrut は「(」の高さと深さを持つ幅 0 の柱。
  • $$ H = H_m + m = -d_m + m = -d + 2m $$\dimen1$$ = H $$$$ -d = $$\dp1、~$$ D = D_m + m = -h_m + m = -h + 2m $$\dimen2$$ = -D $$$$ h = $$\ht1
    また、\dimen0$$ = 2m $$$$ h_0 = $$\ht0$$ d_0 = - $$\dp0
    よって、
    • 11行目、\dimen0\ht0\dp0~
    • 12行目、\dimen1\dp1\dimen0~
    • 13行目、\dimen2\ht1\dimen0~

課題 EditToHeaderToFooter

  • IE と Firefox では ex と em の扱いが異なり、共通の CSS による中線揃えは不可能。
    • プラグインの中で PHP コードレベルでブラウザを識別し、CSS を切り替えるべき?
  • 中線を図の中央にすると、行間に余分な余白が生じる。
    • 余白あった方が行のバランスが良くないか?
    • 無くすとしたら、TeX から数式の高さと深さの情報を出力し、CSS に反映すれば可能か?
fileTexEqMiddleAlign.png 90件 [詳細]
リロード   新規 編集 凍結 差分 添付 複製 改名   初基 一覧 検索 最新 バックアップ リンク元   ヘルプ   最終更新のRSS
Last-modified: 2013.0212 (火) 1057.3700 (1714d)