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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
TITLE:数式プラグインの導入
#contents
%indent
 
////////////////////////////////////////////////////////////////
* 方針 [#m1f9dc5e]
- 基本仕様は [[wikihouse.com>http://www.wikihouse.com/fem/index.php?PukiWiki]] の 
  [[math2.inc.php>http://www.wikihouse.com/fem/index.php?%BC%AB%BA%EE%A5%D7%A5%E9%A5%B0%A5%A4%A5%F3%2Fmath2.inc.php]] に従う。
-- ただし、ハッシュの衝突が心配のため、数式の同定は生のTeXコードで比較。
-- プラグインの同時呼出しで作業ファイルが衝突するため、全てハッシュ名を用いる。
-- LaTeX、dvipngにより数式画像生成後に、[[Imagemagic>http://www.imagemagick.org/script/index.php]] の convert で画像処理を施す。
- 数式の解像度、位置決めは、それなりに拘る。
-- 少なくとも IE7、IE8、Firefox3 の100% ズームを満足し、Firefox3 の最大ズームに耐えうること。
 
////////////////////////////////////////////////////////////////
* 仕様 [#g2292a0a]
- パスを通した platex、dvipng と convert を要求。
- 引数をそのままLaTeXに渡し、最終的にpng画像に変換して出力。
-- ブロックプラグインの場合は、##align##環境(##\begin(align) 〜 \end(align)##)に渡される。
-- インラインプラグインの場合は、ディスプレイ数式モードに変更されるテキスト数式モード($ \displaystyle 〜 $)に渡される。
- 作業ファイルと出力ファイルの名前には、数式に基づくハッシュキーを使用。
-- ##eq/{ハッシュキー}.eq ## に生のTeX式を保存し、数式の同定に使用。ハッシュキーを同定には使わない。
-- ##eq/{ハッシュキー}.png## に数式画像を出力。
- ブラウザのズーム対策として、画像を高解像度で出力し、##IMG##タグの##width##と##height##で縮小表示。
- インライン数式は垂直中心揃え(##vertical-align: middle;##)。分数線が中心線に重ねるようにTeX側で調節する。
-- dviには位置決めのために数式に枠を付けて出力。convert により枠を除去。
- 数式がコンパイルできない場合、代わりに TeX のエラーを出力。
 
////////////////////////////////////////////////////////////////
* 改造 [#b3d347a1]
- ##plugin/eq.inc.php## を作成して、
    #code(php){{{{
    <?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## を作成して、
    #code(tex){{{{
    \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## を作成して、
    #code(tex){{{{
    \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コードで置換して使用。
 
////////////////////////////////////////////////////////////////
* 参考:柱の算出 [#va87e0bb]
  &attachref(./TexEqMiddleAlign.png,35%);
- TeXでは、「(」に相当する ##\mathstruct## の中心を通る線が分数線などに一致。以下、これを「中線」と呼ぶ。
- 数式画像の範囲は赤線が示すようになっており、中線が図の中心を通る保障は無い。
  数式は中線で揃えるため、中線が図の中央を通ると都合が良い。(要検討)
- 中線を図の中心に合わせるため、柱&eq(H_m);と&eq(D_m);が必要。
-- 柱&eq(H_m = -d_m);は、&eq(\ffd{x}{\ffd{x}{x}});のように、中線基準で下辺が離れている場合に、中線が図の中心になるよう上辺を遠ざける。
-- 柱&eq(D_m = -h_m);は、&eq(\ffd{\ffd{x}{x}}{x});のように、中線基準で上辺が離れている場合に、中線が図の中心になるよう下辺を遠ざける。
- 実際はTeXは基線(ベースライン)基準のため、換算した柱&eq(H);と&eq(D);を使う。
- 高度&eq(h_0);と深度&eq(d_0);の ##\mathstrut## の中心を中線が通るため、基線基準の中線位置は&eq(m=\ffd{h_0 + d_0}{2});と求まる。~
  ここで、##\mathstrut## は「(」の高さと深さを持つ幅 0 の柱。~
  
- &eq(H = H_m + m = -d_m + m = -d + 2m);、##\dimen1##&eq( = H);、&eq(-d = );##\dp1##、~
  &eq(D = D_m + m = -h_m + m = -h + 2m);、##\dimen2##&eq( = -D);、&eq(h = );##\ht1##、~
  また、##\dimen0##&eq( = 2m);、&eq(h_0 = );##\ht0##、&eq(d_0 = -);##\dp0##、~
  よって、
-- 11行目、##\dimen0## = ##\ht0## + ##\dp0##~
-- 12行目、##\dimen1## = ##\dp1## + ##\dimen0##~
-- 13行目、##\dimen2## = ##\ht1## − ##\dimen0##~
 
////////////////////////////////////////////////////////////////
* 課題 [#c8f4d2f8]
- IE と Firefox では ex と em の扱いが異なり、共通の CSS による中線揃えは不可能。
-- プラグインの中で PHP コードレベルでブラウザを識別し、CSS を切り替えるべき?
- 中線を図の中央にすると、行間に余分な余白が生じる。
-- 余白あった方が行のバランスが良くないか?
-- 無くすとしたら、TeX から数式の高さと深さの情報を出力し、CSS に反映すれば可能か?
 
////////////////////////////////////////////////////////////////
fileTexEqMiddleAlign.png 485件 [詳細]
    初基 一覧 検索 最新 バックアップ リンク元   ヘルプ   最終更新のRSS