pukiwiki/数式プラグイン の履歴(No.7)
更新- 履歴一覧
- 差分 を表示
- 現在との差分 を表示
- ソース を表示
- ソフトウェア/pukiwiki/数式プラグイン へ行く。
pukiwiki 用の数式プラグイン†
pukiwiki に TeX で書いた数式を入れられるようにするプラグインです。
特に、数式をインライン svg として表示できるようにしたので、 対応するブラウザでは拡大時や印刷時に高い品質が得られます。
現在、ユーザーエージェントに Mozilla/5 を含む場合にのみ、 インライン svg で数式を表示するようになっています。
それ以外では png ファイルとして数式を表示します。
使用例†
&math(y=ax^2+bx+c);
&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 .);
※長い数式も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.inc.php
<?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;
}
?>
本当は†
日本語も入れられるようにしたいところなんだけど、 svg への変換がうまく行かず止まってる。
あと とか打つと文字間がおかしいような気がするんだけど、 これも保留中。(文字幅情報が真四角を基準に取られているため? f の周りに空白が・・・)
コメント†
Counter: 16710 (from 2010/06/03),
today: 3,
yesterday: 5