pukiwiki/数式プラグイン のバックアップ差分(No.14)

更新


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

#contents

* pukiwiki 用の数式プラグイン [#w6180144]

''[[MathJax を使うバージョン>ソフトウェア/pukiwiki/数式プラグイン#v832d483]]は、これとは別に下の方にあります。''

pukiwiki に TeX で書いた数式を入れられるようにするプラグインです。

特に、数式をインライン svg として表示できるようにしたので、
対応するブラウザでは拡大時や印刷時に高い品質が得られます。

現在、ユーザーエージェントに Mozilla/5 を含む場合にのみ、
インライン svg で数式を表示するようになっています。

それ以外では png ファイルとして数式を表示します。

* 使用例 [#s60f1fc1]

 &math(y=ax^2+bx+c);

&math(y=ax^2+bx+c);

 &math(x=\frac{-b\pm\sqrt{b^2-4ac}}{2a});

&math(x=\frac{-b\pm\sqrt{b^2-4ac}}{2a});

 &math(\left\{\begin{array}{c@{\ }l@{\ \ \ \ \ \ \ \ \ \ \ \ }l}\displaystyle 
 \ROT\bm E+\frac{\PD \bm B}{\PD t}&=\bm 0&\MARU{1}\vspace{2mm}
 \\\DIV \bm B &= 0&\MARU{2}\vspace{2mm}\\\displaystyle\frac{1}{\mu_0}\ROT \bm B
 -\varepsilon_0 \frac{\PD \bm E}{\PD t} &= \bm i&\MARU{3}\vspace{2mm}\\
 \varepsilon_0\DIV \bm E &= \rho&\MARU{4}\\\end{array}\right .);

&math(\left\{\begin{array}{c@{\ }l@{\ \ \ \ \ \ \ \ \ \ \ \ }l}\displaystyle \ROT\bm E+\frac{\PD \bm B}{\PD t}&=\bm 0&\MARU{1}\vspace{2mm}\\\DIV \bm B &= 0&\MARU{2}\vspace{2mm}\\\displaystyle\frac{1}{\mu_0}\ROT \bm B-\varepsilon_0 \frac{\PD \bm E}{\PD t} &= \bm i&\MARU{3}\vspace{2mm}\\\varepsilon_0\DIV \bm E &= \rho&\MARU{4}\\\end{array}\right .);

※長い数式も1行で書かなければならないのがつらいところ

 &math(\left\{\begin{array}{c@{}c@{}c@{}c@{}c@{}c@{}c} 
 a_{11}x_1&+&a_{12}x_2&+\cdots+&a_{1n}x_n&=&b_1 \\ a_{21}x_1&+
 &a_{22}x_2&+\cdots+&a_{2n}x_n&=&b_2 \\ \vdots&&\vdots&&\vdots
 &&\vdots\\ a_{m1}x_1&+&a_{m2}x_2&+\cdots+&a_{mn}x_n&=&b_m \\ 
 \end{array}\right .);

&math(\left\{\begin{array}{c@{}c@{}c@{}c@{}c@{}c@{}c} a_{11}x_1&+&a_{12}x_2&+\cdots+&a_{1n}x_n&=&b_1 \\ a_{21}x_1&+&a_{22}x_2&+\cdots+&a_{2n}x_n&=&b_2 \\ \vdots&&\vdots&&\vdots&&\vdots\\ a_{m1}x_1&+&a_{m2}x_2&+\cdots+&a_{mn}x_n&=&b_m \\ \end{array}\right .);

* ソースコード [#p0757512]

math.inc.php

 LANG:php(linenumber)
 <?php
 //
 // pukiwiki用 数式プラグイン (math.inc.php)
 //   Copywrite 2011 Osamu Takeuchi <osamu@big.jp>
 //
 // [履歴]
 //   2011.05.04 初期リリース
 //
 // [インストール]
 //   ソースファイルを (pukiwiki)/plugin/math.inc.php として保存
 //
 // [設定]
 //   MATHIMG_DIR に画像ファイルが作成される
 //       標準では (pukiwiki)/mathimg に画像ファイルが作成される
 //   function user_macros() に TeX マクロを記述できる
 //   function use_svg() でインライン svg を使うかどうかを判断する
 //
 // [使い方]
 //   &math(y=ax^2+bx+c); のように、&math(); 中に TeX ソース形式で
 //   数式を記述する
 //   TeX ソースとしては amsmath, amssymb, bm パッケージを利用し、
 //   equation + split 環境で評価される
 //
 
 //math mode image repository
 define("MATHIMG_DIR", "./mathimg");
 
 function use_svg()
 {
     return strstr( getenv("HTTP_USER_AGENT"), "Mozilla/5" ) != false;
 }
 
 function user_macros()
 {
     return 
        '\newcommand{\MARU}[1]
         {{\ooalign{\hfil#1\/\hfil\crcr\raise.167ex\hbox{\mathhexbox20D}}}}
         \newcommand{\D}[0]{{\rm d}}
         \newcommand{\PD}[0]{\partial}
         \newcommand{\ROT}[0]{{\rm rot}\,}
         \newcommand{\DIV}[0]{{\rm div}\,}
         \newcommand{\GRAD}[0]{{\rm grad}\,}
         ';
 }
 
 function math2tex($math){
     return 
        '\documentclass[12pt,fleqn]{jarticle}
         \pagestyle{empty}
         \usepackage{amsmath,amssymb,bm}
         \begin{document}
         \setlength{\hoffset}{-1in}
         \setlength{\voffset}{-1in}
         \setlength{\topmargin}{0mm}
         \setlength{\headheight}{0mm}
         \setlength{\headsep}{0mm}
         \setlength{\oddsidemargin}{0mm}
         \setlength{\mathindent}{0mm}
         ' . user_macros() .
        '\begin{equation*}
         \begin{split}
         ' . $math .
        '\end{split}
         \end{equation*}
         \end{document}';
 }
 
 class MinMax {
     var $min = null;
     var $max = null;
     function update($x){
         if (is_null($this->min)) {
             $this->min = $x;
             $this->max = $x;
         } else
         if ($x<$this->min) {
             $this->min = $x;
         } else
         if ($this->max<$x) {
             $this->max = $x;
         }
     }
     function min()
     { return $this->min; }
     function max()
     { return $this->max; }
 }
 
 function calc_bounding_box($math, $srch)
 {
     $xmm = new MinMax();
     $ymm = new MinMax();
     $converted = array();
     while(!feof($srch)) {
         $line = fgets($srch);
         if (preg_match("/<g /", $line, $matches)) {
             $converted[] = preg_replace("/ transform=\"[^\"]*\"/", "", $line);
         } else
         if (preg_match("/<\/g>/", $line, $matches)) {
             $converted[] = $line;
         } else
         if (preg_match("/<line (.*)/", $line, $matches)) {
             $params = $matches[1];
             if (preg_match("/x1=\"([^\"]+)\"/", $params, $matches))
                 $xmm->update($matches[1]);
             if (preg_match("/y1=\"([^\"]+)\"/", $params, $matches))
                 $ymm->update($matches[1]);
             if (preg_match("/x2=\"([^\"]+)\"/", $params, $matches))
                 $xmm->update($matches[1]);
             if (preg_match("/y2=\"([^\"]+)\"/", $params, $matches))
                 $ymm->update($matches[1]);
             $converted[] = $line;
         } else
         if (preg_match("/<polygon points=\"([^\"]+)\"/", $line, $matches)) {
             foreach (explode(" ", $matches[1]) as $p) {
                 foreach (explode(",", $p) as $i => $v) {
                     if ($i % 2 == 0) {
                         $xmm->update($v);
                     } else {
                         $ymm->update($v);
                     }
                 }
             }
             $converted[] = $line;
         } else
         if (preg_match("/<path d=\"([^\"]+)\"/", $line, $matches)) {
             foreach (explode(" ", $matches[1]) as $c) {
                 if (preg_match("/^(M|L|C|S|Q|T)([0-9\\.,]+)/", $c, $matches)) {
                     foreach (explode(",", $matches[2]) as $i => $v) {
                         if ($i % 2 == 0) {
                             $xmm->update($v);
                         } else {
                             $ymm->update($v);
                         }
                     }
                 } else
                 if (preg_match("/^H([0-9\\.]+)/", $c, $matches)) {
                     $xmm->update($matches[1]);
                 } else
                 if (preg_match("/^V([0-9\\.]+)/", $c, $matches)) {
                     $ymm->update($matches[1]);
                 }
             }
             $converted[] = $line;
         }
     }
     return array( $xmm, $ymm, implode($converted) );
 }
 
 function format_svg($math, $srch)
 {
     list( $xmm, $ymm, $converted ) = calc_bounding_box($math, $srch);
 
     $margin= 0.02;
     $scale= 1.35;
 
     $math_encoded = htmlspecialchars($math);
     $width  = ( $xmm->max() - $xmm->min() + $margin*2 ) * $scale;
     $height = ( $ymm->max() - $ymm->min() + $margin*2 ) * $scale;
     $transx = - ( $xmm->min() - $margin );
     $transy = - ( $ymm->max() + $margin );
     return
         "<svg width=\"{$width}\" height=\"{$height}\" " .
         "xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" " .
         "style=\"vertical-align:middle;\" >\n" .
         "<title>{$math_encoded}</title>\n" .
         "<g transform=\"scale({$scale},-{$scale}) translate({$transx}, {$transy})\">\n" .
         "{$converted}\n" .
         "</g></svg>\n";
 }
 
 function create_image_files($math, $image_dir, $image_png, $image_svg)
 {
     if(!file_exists($image_dir))
         mkdir($image_dir, 0770, true);
 
     $tmp_dir = sys_get_temp_dir();
     $tmp_base = $tmp_dir . "/pukiwiki_math_plugin" . getmypid();
     $tmp_tex =  $tmp_base . ".tex";
     $tmp_dvi =  $tmp_base . ".dvi";
     $tmp_any =  $tmp_base . ".*";
     try {
         # create tex src
         $texh = fopen($tmp_tex, 'w');
         fwrite($texh, math2tex($math));
         fclose($texh);
 
         # convert tex to dvi
         system("jlatex -interaction=nonstopmode -output-directory={$tmp_dir} {$tmp_tex} > /dev/null");
         # create png
         system("dvipng -q -bdpi 1440 -o {$image_png} {$tmp_dvi} > /dev/null");
         # create svg
         $pp = popen("dvips -q -Ppdf -E {$tmp_dvi} -o - " .
                     "|pstoedit -q -f plot-svg -dt -sclip -ssp - -", 'r');
         $svg = format_svg($math, $pp);
         pclose($pp);
         # output formatted svg
         $svgh = fopen($image_svg, "w");
         fwrite($svgh, $svg);
         fclose($svgh);
     } catch(Exception $e) {
         # cleanup temporary files
         system("rm " . $tmp_any);
         throw $e;
     }
 }
 
 function math2html($math)
 {
 #    if(strlen(encode($math))<40){
 #        $image_base = encode($math);
 #    }else{
         $image_base = sha1($math);
 #    }
     $image_dir = MATHIMG_DIR . "/{$image_base[0]}/{$image_base[1]}";
     $image_png = "{$image_dir}/{$image_base}.png";
     $image_svg = "{$image_dir}/{$image_base}.svg";
 
     if(!file_exists($image_svg))
         create_image_files($math, $image_dir, $image_png, $image_svg);
 
     if(use_svg()) {
         $fh = fopen($image_svg, "r");
         $svg = fread($fh, filesize($image_svg));
         fclose($fh);
         return $svg;
     } else {
         list($w, $h) = getimagesize( $image_png );
         $math_encoded = htmlspecialchars($math);
         if($w > 0 && $h > 0){
               return "<img src=\"{$image_png}\" alt=\"{$math_encoded}\" " .
                      "width=\"{$w}\" height=\"{$h}\">";
         }else{
               return "<img src=\"{$image_png}\" alt=\"{$math_encoded}\">";
         }
     }
 }
 
 function plugin_math_convert()
 {
     return plugin_math_inline();
 }
 
 function plugin_math_inline()
 {
     $aryargs = func_get_args();
     $math = join(",", $aryargs);
     $math = rtrim($math, ",");  //remove extra comma at the end.
     $html = math2html($math);
     return $html;
 }
 
 ?>

* 本当は [#v850a92f]

日本語も入れられるようにしたいところなんだけど、
svg への変換がうまく行かず止まってる。

あと &math(minbfg); とか打つと文字間がおかしいような気がするんだけど、
これも保留中。(文字幅情報が真四角を基準に取られているため? f の周りに空白が・・・)

* MathJax [#df91d1aa]

MathJax というのがあるらしい。

MathJax http://www.mathjax.org/ ~
MathJaxの使い方 http://genkuroki.web.fc2.com/ ~
MathJax の導入試験 http://www.str.ce.akita-u.ac.jp/~gotou/mathjax/ ~

こっちに切換えることを検討したいかも。

* %%現在、MathJax を使用する形にして様子見中%% [#h007b5b5]

\bm が見にくい点が、線形代数をやるのに致命的か?

いろいろ変換をミスっているところがありそうなので、見直さなければ。

** これ、重〜い! [#u6d7279a]

最新の PC ならまだ良いけれど、タブレットで [[スピントロニクス理論の基礎/8-10]] 
なんかを開くと分単位で待たされる?!

サーバーの負担は軽くなるし、見た目はまあ悪くないし、もうしばらく経ったら行けそうだけど、
どうするかなあ・・・

** 非互換性 [#q9e73e98]

- \ooalign → 丸付文字は代わりに \enclose を使えば良さそう
- \vspace
- \bra, \ket, \braket : from braket.sty
- 不等号の直後に変数が入って <t のようになっているときにうまく変換されない?
-- プラグインからの出力を htmlspecialchars に通していなかったのが原因だった
- 互換性情報 http://www.wikidot.com/doc:math

** 元に戻した [#qb22342a]

悪くはないのだけれど、
- 重すぎるのと
- \bm が見にくいこと

とが気になって元に戻した。

* mathjax プラグイン [#v832d483]

 LANG:php
 <?php
 //
 // pukiwiki用 数式プラグイン (mathjax.inc.php)
 //   Copywrite 2013 Osamu Takeuchi <osamu@big.jp>
 //
 // [履歴]
 //   2013.04.08 初期リリース
 //
 // [インストール]
 //   ソースファイルを (pukiwiki)/plugin/mathjax.inc.php として保存
 //
 // [使い方]
 //   #mathjax; として一回呼んでおくと、その後の TeX ソース内に
 //   記述された mathjax 形式の tex ソースを maxjax で処理する
 //   ようになる
 //
 
 function plugin_mathjax_header()
 {
     return <<<'EOS'
 <meta http-equiv="X-UA-Compatible" CONTENT="IE=EmulateIE7" />
 <script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-MML-AM_HTMLorMML"></script>
 <script type="text/x-mathjax-config">
   MathJax.Hub.Config({
     tex2jax: {
       inlineMath: [ ['$','$'], ["\\(","\\)"] ],
       displayMath: [ ['$$','$$'], ["\\[","\\]"] ]
     },
     TeX: {
       extensions: ["cancel.js","enclose.js","mhchem.js"],
       Macros: {
         C: '{\\mathbb C}',
         R: '{\\mathbb R}',
         Q: '{\\mathbb Q}',
         Z: '{\\mathbb Z}',
         diag: '\\mathop{\\mathrm{diag}}\\nolimits'
       }
     }
   });
 </script>
 EOS;
 }
 
 function plugin_mathjax_convert()
 {
     return plugin_math_inline();
 }
 
 function plugin_mathjax_inline()
 {
     $header = "";
     if (!defined("PLUGIN_MATHJAX_LOADED")) {
         define("PLUGIN_MATHJAX_LOADED", "LOADED");
         $header = plugin_mathjax_header();
     }
 
     $aryargs = func_get_args();
     $math = join(",", $aryargs);
     $math = rtrim($math, ",");  //remove extra comma at the end.
     $math = htmlspecialchars($math);
 
     if($math===""){
         return $header;
     }else{
         return $header . "\\(\\displaystyle\\begin{split}" . $math . "\\end{split}\\)";
     }
 }
 
 ?>

&mathjax; これを入れるだけで、\[x=\frac{-b\pm\sqrt{b^2-4ac}}{2a}\] などと書けるようになる。

事前に定義しておきたい内容があれば Macros のところに追加すればいい。

たとえば、

        MARU: ['\\enclose{circle}{\\scriptsize #1}', 1],
        tr: '\\mathop{\\mathrm{tr}}\\nolimits',
        rank: '\\mathop{\\mathrm{rank}}\\nolimits',
        grad: '\\mathop{\\mathrm{grad}}\\nolimits',
        rot: '\\mathop{\\mathrm{rot}}\\nolimits',
        divergence: '\\mathop{\\mathrm{div}}\\nolimits'

とか。

* コメント [#od7d9c0b]

#article_kcaptcha
**sryrzZIrlX [#x8920292]
>[Yolanda] (2013-04-11 (木) 09:02:52)~
~
Hey, that's pwoerufl. Thanks for the news.~

//

#comment_kcaptcha


Counter: 13087 (from 2010/06/03), today: 1, yesterday: 2