Math extension:

*coding style tweaks
*documentation
*check for MediaWiki environment in the main setup file
*add extension credits
*add i18n file (only English at the moment); messages from core MessagesEn.php

This is a follow-up to Brion's r85706.
This commit is contained in:
Jack Phoenix 2011-04-09 15:13:22 +00:00
parent d690d96e24
commit e0b832c637
4 changed files with 176 additions and 74 deletions

View file

@ -5,7 +5,7 @@
* (c) 2002-2011 Tomasz Wegrzanowski, Brion Vibber, and other MediaWiki contributors
* GPLv2 license; info in main package.
*
* Contain everything related to <math> </math> parsing
* Contains everything related to <math> </math> parsing
* @file
* @ingroup Parser
*/
@ -27,7 +27,7 @@ class MathRenderer {
var $mathml = '';
var $conservativeness = 0;
function __construct( $tex, $params=array() ) {
function __construct( $tex, $params = array() ) {
$this->tex = $tex;
$this->params = $params;
}
@ -80,46 +80,46 @@ class MathRenderer {
$contents = wfShellExec( $cmd );
wfDebug( "TeX output:\n $contents\n---\n" );
if (strlen($contents) == 0) {
if ( strlen( $contents ) == 0 ) {
return $this->_error( 'math_unknown_error' );
}
$retval = substr ($contents, 0, 1);
$retval = substr( $contents, 0, 1 );
$errmsg = '';
if (($retval == 'C') || ($retval == 'M') || ($retval == 'L')) {
if ($retval == 'C') {
if ( ( $retval == 'C' ) || ( $retval == 'M' ) || ( $retval == 'L' ) ) {
if ( $retval == 'C' ) {
$this->conservativeness = 2;
} else if ($retval == 'M') {
} elseif ( $retval == 'M' ) {
$this->conservativeness = 1;
} else {
$this->conservativeness = 0;
}
$outdata = substr ($contents, 33);
$outdata = substr( $contents, 33 );
$i = strpos($outdata, "\000");
$i = strpos( $outdata, "\000" );
$this->html = substr($outdata, 0, $i);
$this->mathml = substr($outdata, $i+1);
} else if (($retval == 'c') || ($retval == 'm') || ($retval == 'l')) {
$this->html = substr ($contents, 33);
if ($retval == 'c') {
$this->html = substr( $outdata, 0, $i );
$this->mathml = substr( $outdata, $i + 1 );
} elseif ( ( $retval == 'c' ) || ( $retval == 'm' ) || ( $retval == 'l' ) ) {
$this->html = substr( $contents, 33 );
if ( $retval == 'c' ) {
$this->conservativeness = 2;
} else if ($retval == 'm') {
} elseif ( $retval == 'm' ) {
$this->conservativeness = 1;
} else {
$this->conservativeness = 0;
}
$this->mathml = null;
} else if ($retval == 'X') {
} elseif ( $retval == 'X' ) {
$this->html = null;
$this->mathml = substr ($contents, 33);
$this->mathml = substr( $contents, 33 );
$this->conservativeness = 0;
} else if ($retval == '+') {
} elseif ( $retval == '+' ) {
$this->html = null;
$this->mathml = null;
$this->conservativeness = 0;
} else {
$errbit = htmlspecialchars( substr($contents, 1) );
$errbit = htmlspecialchars( substr( $contents, 1 ) );
switch( $retval ) {
case 'E':
$errmsg = $this->_error( 'math_lexing_error', $errbit );
@ -136,16 +136,16 @@ class MathRenderer {
}
if ( !$errmsg ) {
$this->hash = substr ($contents, 1, 32);
$this->hash = substr( $contents, 1, 32 );
}
wfRunHooks( 'MathAfterTexvc', array( &$this, &$errmsg ) );
if ( $errmsg ) {
return $errmsg;
return $errmsg;
}
if (!preg_match("/^[a-f0-9]{32}$/", $this->hash)) {
if ( !preg_match( "/^[a-f0-9]{32}$/", $this->hash ) ) {
return $this->_error( 'math_unknown_error' );
}
@ -175,19 +175,22 @@ class MathRenderer {
# Now save it back to the DB:
if ( !wfReadOnly() ) {
$outmd5_sql = pack('H32', $this->hash);
$outmd5_sql = pack( 'H32', $this->hash );
$md5_sql = pack('H32', $this->md5); # Binary packed, not hex
$md5_sql = pack( 'H32', $this->md5 ); # Binary packed, not hex
$dbw = wfGetDB( DB_MASTER );
$dbw->replace( 'math', array( 'math_inputhash' ),
array(
'math_inputhash' => $dbw->encodeBlob($md5_sql),
'math_outputhash' => $dbw->encodeBlob($outmd5_sql),
'math_html_conservativeness' => $this->conservativeness,
'math_html' => $this->html,
'math_mathml' => $this->mathml,
), __METHOD__
$dbw->replace(
'math',
array( 'math_inputhash' ),
array(
'math_inputhash' => $dbw->encodeBlob( $md5_sql ),
'math_outputhash' => $dbw->encodeBlob( $outmd5_sql ),
'math_html_conservativeness' => $this->conservativeness,
'math_html' => $this->html,
'math_mathml' => $this->mathml,
),
__METHOD__
);
}
@ -204,7 +207,7 @@ class MathRenderer {
}
function _error( $msg, $append = '' ) {
$mf = htmlspecialchars( wfMsg( 'math_failure' ) );
$mf = htmlspecialchars( wfMsg( 'math_failure' ) );
$errmsg = htmlspecialchars( wfMsg( $msg ) );
$source = htmlspecialchars( str_replace( "\n", ' ', $this->tex ) );
return "<strong class='error'>$mf ($errmsg$append): $source</strong>\n";
@ -215,16 +218,22 @@ class MathRenderer {
$this->md5 = md5( $this->tex );
$dbr = wfGetDB( DB_SLAVE );
$rpage = $dbr->selectRow( 'math',
array( 'math_outputhash','math_html_conservativeness','math_html','math_mathml' ),
array( 'math_inputhash' => $dbr->encodeBlob(pack("H32", $this->md5))), # Binary packed, not hex
$rpage = $dbr->selectRow(
'math',
array(
'math_outputhash', 'math_html_conservativeness', 'math_html',
'math_mathml'
),
array(
'math_inputhash' => $dbr->encodeBlob( pack( "H32", $this->md5 ) ) # Binary packed, not hex
),
__METHOD__
);
if( $rpage !== false ) {
# Tailing 0x20s can get dropped by the database, add it back on if necessary:
$xhash = unpack( 'H32md5', $dbr->decodeBlob($rpage->math_outputhash) . " " );
$this->hash = $xhash ['md5'];
$xhash = unpack( 'H32md5', $dbr->decodeBlob( $rpage->math_outputhash ) . " " );
$this->hash = $xhash['md5'];
$this->conservativeness = $rpage->math_html_conservativeness;
$this->html = $rpage->math_html;
@ -261,11 +270,11 @@ class MathRenderer {
} elseif( !is_dir( $hashpath ) || !is_writable( $hashpath ) ) {
return false;
}
if ( function_exists( "link" ) ) {
return link ( $wgMathDirectory . "/{$this->hash}.png",
if ( function_exists( 'link' ) ) {
return link( $wgMathDirectory . "/{$this->hash}.png",
$hashpath . "/{$this->hash}.png" );
} else {
return rename ( $wgMathDirectory . "/{$this->hash}.png",
return rename( $wgMathDirectory . "/{$this->hash}.png",
$hashpath . "/{$this->hash}.png" );
}
}
@ -286,9 +295,11 @@ class MathRenderer {
array( 'xmlns' => 'http://www.w3.org/1998/Math/MathML' ) ),
$this->mathml );
}
if (($this->mode == MW_MATH_PNG) || ($this->html == '') ||
(($this->mode == MW_MATH_SIMPLE) && ($this->conservativeness != 2)) ||
(($this->mode == MW_MATH_MODERN || $this->mode == MW_MATH_MATHML) && ($this->conservativeness == 0))) {
if ( ( $this->mode == MW_MATH_PNG ) || ( $this->html == '' ) ||
( ( $this->mode == MW_MATH_SIMPLE ) && ( $this->conservativeness != 2 ) ) ||
( ( $this->mode == MW_MATH_MODERN || $this->mode == MW_MATH_MATHML ) && ( $this->conservativeness == 0 ) )
)
{
return $this->_linkToMathImage();
} else {
return Xml::tags( 'span',
@ -296,11 +307,12 @@ class MathRenderer {
array( 'class' => 'texhtml',
'dir' => 'ltr'
) ),
$this->html );
$this->html
);
}
}
function _attribs( $tag, $defaults=array(), $overrides=array() ) {
function _attribs( $tag, $defaults = array(), $overrides = array() ) {
$attribs = Sanitizer::validateTagAttributes( $this->params, $tag );
$attribs = Sanitizer::mergeAttributes( $defaults, $attribs );
$attribs = Sanitizer::mergeAttributes( $attribs, $overrides );
@ -315,9 +327,13 @@ class MathRenderer {
'img',
array(
'class' => 'tex',
'alt' => $this->tex ),
'alt' => $this->tex
),
array(
'src' => $url ) ) );
'src' => $url
)
)
);
}
function _mathImageUrl() {
@ -328,21 +344,22 @@ class MathRenderer {
function _getHashPath() {
global $wgMathDirectory;
$path = $wgMathDirectory .'/' . $this->_getHashSubPath();
$path = $wgMathDirectory . '/' . $this->_getHashSubPath();
wfDebug( "TeX: getHashPath, hash is: $this->hash, path is: $path\n" );
return $path;
}
function _getHashSubPath() {
return substr($this->hash, 0, 1)
.'/'. substr($this->hash, 1, 1)
.'/'. substr($this->hash, 2, 1);
return substr( $this->hash, 0, 1)
. '/' . substr( $this->hash, 1, 1 )
. '/' . substr( $this->hash, 2, 1 );
}
public static function renderMath( $tex, $params=array(), ParserOptions $parserOptions = null ) {
public static function renderMath( $tex, $params = array(), ParserOptions $parserOptions = null ) {
$math = new MathRenderer( $tex, $params );
if ( $parserOptions )
if ( $parserOptions ) {
$math->setOutputMode( $parserOptions->getMath() );
}
return $math->render();
}
}

View file

@ -7,30 +7,55 @@
*/
class MathHooks {
/**
* Set up $wgMathPath and $wgMathDirectory globals if they're not already
* set.
*/
static function setup() {
global $wgMathPath, $wgMathDirectory;
global $wgUploadPath, $wgUploadDirectory;
if ( $wgMathPath === false ) $wgMathPath = "{$wgUploadPath}/math";
if ( $wgMathDirectory === false ) $wgMathDirectory = "{$wgUploadDirectory}/math";
if ( $wgMathPath === false ) {
$wgMathPath = "{$wgUploadPath}/math";
}
if ( $wgMathDirectory === false ) {
$wgMathDirectory = "{$wgUploadDirectory}/math";
}
}
static function onParserFirstCallInit($parser)
{
/**
* Register the <math> tag with the Parser.
*
* @param $parser Object: instance of Parser
* @return Boolean: true
*/
static function onParserFirstCallInit( $parser ) {
$parser->setHook( 'math', array( 'MathHooks', 'mathTagHook' ) );
return true;
}
/**
* @param $content
* @param $attributes
* Callback function for the <math> parser hook.
*
* @param $content
* @param $attributes
* @param $parser Parser
* @return
*/
static function mathTagHook( $content, $attributes, $parser ) {
global $wgContLang;
return $wgContLang->armourMath( MathRenderer::renderMath( $content, $attributes, $parser->getOptions() ) );
$renderedMath = MathRenderer::renderMath(
$content, $attributes, $parser->getOptions()
);
return $wgContLang->armourMath( $renderedMath );
}
/**
* Add the new math rendering options to Special:Preferences.
*
* @param $user Object: current User object
* @param $defaultPreferences Object: Preferences object
* @return Boolean: true
*/
static function onGetPreferences( $user, &$defaultPreferences ) {
global $wgLang;
$defaultPreferences['math'] = array(

38
Math.i18n.php Normal file
View file

@ -0,0 +1,38 @@
<?php
/**
* Internationalization file for the Math extension.
*
* @file
* @ingroup Extensions
*/
$messages = array();
/** English */
$messages['en'] = array(
// Edit toolbar stuff shown on ?action=edit (example text & tooltip)
'math_sample' => 'Insert formula here',
'math_tip' => 'Mathematical formula (LaTeX)',
// Header on Special:Preferences (or something)
'prefs-math' => 'Math',
// Math options
'mw_math_png' => 'Always render PNG',
'mw_math_simple' => 'HTML if very simple or else PNG',
'mw_math_html' => 'HTML if possible or else PNG',
'mw_math_source' => 'Leave it as TeX (for text browsers)',
'mw_math_modern' => 'Recommended for modern browsers',
'mw_math_mathml' => 'MathML if possible (experimental)',
// Math errors
'math_failure' => 'Failed to parse',
'math_unknown_error' => 'unknown error',
'math_unknown_function' => 'unknown function',
'math_lexing_error' => 'lexing error',
'math_syntax_error' => 'syntax error',
'math_image_error' => 'PNG conversion failed; check for correct installation of latex and dvipng (or dvips + gs + convert)',
'math_bad_tmpdir' => 'Cannot write to or create math temp directory',
'math_bad_output' => 'Cannot write to or create math output directory',
'math_notexvc' => 'Missing texvc executable; please see math/README to configure.',
);

View file

@ -2,21 +2,41 @@
/**
* MediaWiki math extension
*
* (c) 2002-2011 various MediaWiki contributors
* GPLv2 license; info in main package.
* @file
* @ingroup Extensions
* @version 1.0
* @author Tomasz Wegrzanowski
* @author Brion Vibber
* @copyright © 2002-2011 various MediaWiki contributors
* @license GPLv2 license; info in main package.
* @link http://www.mediawiki.org/wiki/Extension:Math Documentation
* @see https://bugzilla.wikimedia.org/show_bug.cgi?id=14202
*/
if ( !defined( 'MEDIAWIKI' ) ) {
die( "This is not a valid entry point to MediaWiki.\n" );
}
// Extension credits that will show up on Special:Version
$wgExtensionCredits['parserhook'][] = array(
'name' => 'Math',
'version' => '1.0',
'author' => array( 'Tomasz Wegrzanowski', 'Brion Vibber', '...' ),
'description' => 'Render mathematical formulas between <code>&lt;math&gt;</code> ... <code>&lt;/math&gt;</code> tags',
'url' => 'http://www.mediawiki.org/wiki/Extension:Math',
);
/** For back-compat */
$wgUseTeX = true;
/** Location of the texvc binary */
$wgTexvc = dirname( __FILE__ ) . '/math/texvc';
/**
* Texvc background color
* use LaTeX color format as used in \special function
* for transparent background use value 'Transparent' for alpha transparency or
* 'transparent' for binary transparency.
*/
* Texvc background color
* use LaTeX color format as used in \special function
* for transparent background use value 'Transparent' for alpha transparency or
* 'transparent' for binary transparency.
*/
$wgTexvcBackgroundColor = 'transparent';
/**
@ -32,14 +52,13 @@ $wgTexvcBackgroundColor = 'transparent';
*/
$wgMathCheckFiles = true;
/**
* The URL path of the math directory. Defaults to "{$wgUploadPath}/math".
*
* See http://www.mediawiki.org/wiki/Manual:Enable_TeX for details about how to
* set up mathematical formula display.
*/
$wgMathPath = false;
$wgMathPath = false;
/**
* The filesystem path of the math directory.
@ -48,7 +67,7 @@ $wgMathPath = false;
* See http://www.mediawiki.org/wiki/Manual:Enable_TeX for details about how to
* set up mathematical formula display.
*/
$wgMathDirectory = false;
$wgMathDirectory = false;
////////// end of config settings.
@ -58,7 +77,10 @@ $wgExtensionFunctions[] = 'MathHooks::setup';
$wgHooks['ParserFirstCallInit'][] = 'MathHooks::onParserFirstCallInit';
$wgHooks['GetPreferences'][] = 'MathHooks::onGetPreferences';
$wgAutoloadClasses['MathHooks'] = dirname( __FILE__ ) . '/Math.hooks.php';
$wgAutoloadClasses['MathRenderer'] = dirname( __FILE__ ) . '/Math.body.php';
$dir = dirname( __FILE__ ) . '/';
$wgAutoloadClasses['MathHooks'] = $dir . 'Math.hooks.php';
$wgAutoloadClasses['MathRenderer'] = $dir . 'Math.body.php';
$wgParserTestFiles[] = dirname( __FILE__ ) . "/mathParserTests.txt";
$wgExtensionMessagesFiles['Math'] = $dir . 'Math.i18n.php';
$wgParserTestFiles[] = $dir . 'mathParserTests.txt';